Compare commits

...

366 commits

Author SHA1 Message Date
3cdf7767eb Updated Crumble Undead and created Snowscape API
New API will allow creating new global functions without conflicting with upstream changes.
Crumble undead now works on more undead, including Barrows brothers and revenants.
2025-12-25 21:10:40 -07:00
e9085a8270 Fixed ::dps command taking the melee attack speed of the staff instead of the autocast speed when using magic.
Also shortened the command description so it fits on the ::commands list.
2025-08-26 21:15:55 -06:00
bfa62864fa Implemented custom ::dps command to combine the information from ::calcmaxhit and ::calcaccuracy 2025-08-25 16:00:14 -06:00
b372d5e963 Players can now freely swap between unlocked spellbooks with the magic interface sort buttons.
The "Level Order" button is the Modern spellbook, "Combat First" is Ancient, and "Teleports first" is Lunar.
This makes the ability to swap spellbooks with the Mage's book obsolete, so that feature has been removed.
2025-08-25 10:55:37 -06:00
590e9fe44f Telekinetic grab now grabs all matching items within 10 tiles of the target item
If casting without line of sight, the player will now walk closer rather than simply being unable to cast
2025-08-20 13:23:00 -06:00
59c55708a6 Improvements to autolooting blacklist.
Since player names are shorter than many item names, items will now be prevented from being looted if the beginning of the name is in the block list.
The blocklist now also prevents satchels and the ring of wealth from looting items, not just familiars as it did previously.
2025-08-19 11:53:24 -06:00
5a896cecb4 Alchemy Improvements
Experimental note spell that replaced low level alchemy was removed. Now, Low level alchemy casts faster and alchemizes 10 items per cast, making it a viable alternative to alchemize cheap items in bulk.
Both alchemy spells continuous cast time was reduced from 90 to 60 seconds, but can be refreshed at any time by manually casting again.
Explorer's ring was improved. It no longer has a dialogue prompt and also alchemizes 10 items per cast, same as the spell version.
2025-08-18 14:26:19 -06:00
e046f46ed2 Slower melee weapons now do a minimum damage on successful hits to compensate for the lower dps.
The minimum damage is 10% of the max hit for each tick below scimitar speeds (rounded down). This leads to a maximum dps increase of 10% for 5 tick weapons, 22% for 6 tick, and 40% for 7 tick. Usually less though, due to the rounding.
Also added the formula to the ::calcmaxhit command so players can see the minimum hit in game.
2025-08-18 08:29:34 -06:00
2ecdde1011 The Fairy Ring interface now shows all destinations, not just the ones you have visited before. 2025-08-17 09:21:30 -06:00
cd8cce30a0 Fixed Plain Satchel not looting noted items 2025-08-16 20:06:07 -06:00
06055c0ff8 Remove another check preventing Ancient magick teleport spells from working in the wilderness for non-admins. 2025-08-15 11:41:12 -06:00
6e91725d58 Ghostly Robes now also prevent NPC aggro when the full set is worn
This is shared with the Protect from Summoning prayer. In addition, the effect was improved so a hidden player cannot shield another player from aggro by standing between them and the NPC
Fixed missing animations and sounds for the Shadow Sword
2025-08-15 11:03:21 -06:00
a9065d763b Even more steps to remove random events 2025-08-14 16:56:30 -06:00
2ad90e9ed8 Fixed Combiniation rune consumption and rebalanced rune satchel
Combination runes have been fixed. A spell using 1 air and 1 earth rune will now only use 1 dust rune instead of 2.
Rune satchel has been changed. Instead of allowing only catalytic runes, it now allows all runes but only has 6 slots. In addition, combination runes can be used from the satchel.
2025-08-14 16:47:56 -06:00
cecdb97b19 Several Housing improvements
Can enter house from any house portal in the world, and can exit to any portal the owner has the construction level to move their house to.
Can enter house even if owner is not home. House is no longer unloaded when the owner leaves, instead is unloaded when the last player leaves. Guests are kicked when owner logs out.
Locking house persists after logout/login. Using the Building mode buttons when outside the house allows remotely locking/unlocking the house.
Teleport to House and Tablet now teleport to the last house that was entered through a house portal, if it is online and unlocked. Falls back to your own house if that fails.
2025-05-13 14:29:51 -06:00
52185005aa Enchanting spells repeat if there are more of the same item in the player inventory 2025-05-06 10:57:31 -06:00
7e8c2d8883 Herblore makes potions with 4 doses instead of 3. 2025-04-21 11:36:02 -06:00
af446a4c27 Changed Construction Workbenches to no longer require upgrading
Since hotspot upgrading is not in, they were requiring an unobtainable item in order to craft.
They now only require oak planks and steel bars. The ammounts have been increased to compensate for the lack of upgrade mechanic.
2025-04-17 09:47:30 -06:00
67990205b4 Fixed Ibis not rewarding exp when catching swordfish 2025-04-12 08:31:36 -06:00
fa429edc03 Fixed Ibis familiar foraging raw fish instead of cooked fish 2025-04-12 07:43:03 -06:00
3f0c44b1c3 Firemaking improments
Improved the firemaking repeat pulse. Logs are now consumed after the space is verified, preventing loss of logs when running out of room. Additionally, the timing has changed to match the animation better.
Firemaking now also repeats when done by using a log on a pyrelord. This requires a tinderbox in your inventory, so that the pulse can be interrupted by dropping it.
Also fixed the bug where pyrelords could light logs you didn't have the firemaking level for.
2025-04-11 12:07:51 -06:00
b3196a282c Implemented construction training in workshop
As a temporary feature until making flatpacks is implemented, working at a workbench will simply consume planks for construction exp. Less clicking than creating and destroying tables over and over.
Each plank type requires a specific construction level and workbench tier to use.
2025-04-02 11:42:28 -06:00
4053fdf43c Fixed null pointer exception when runecrafting without a familiar 2025-04-01 11:58:25 -06:00
415aa512fb Satchels can now be emptied by using on a banker npc
This is to address some banks (like zanaris) not having a bank booth
2025-04-01 11:55:26 -06:00
54055e36d0 Runecrafting pouch improvements
Using any of the essence pouches on a bank booth, bank chest, or banker npc will refill all the pouches in the player inventory, as well as their familiar.
When runecrafting, if the player runs out of essence then essence will be withdrawn from the familiar and pouches.
Both of the previous functions only work on pure essence, not rune essence.
Also moved the Ourania teleport closer to the altar.
2025-04-01 11:47:08 -06:00
269eb82850 Added bank tab handling for the Costume Room Bank 2025-04-01 08:46:49 -06:00
4d7f26bb47 Disabled Easter Event 2025-04-01 08:38:50 -06:00
7538322006 Implemented Costume Room Bank
Interacting with the fancy dress box in the costume room of a player owned house will allow access to this extra storage.
Items stored in this bank count as "worn" when doing treasure trail emote clues.
2025-03-31 13:03:41 -06:00
08ffa6fee6 Crafting Gold Jewellery will automatically take gems from the Gold Satchel 2025-03-30 15:53:05 -06:00
randy
bfbfae8391 Merge pull request 'Update from 2009scape' (#18) from 2009Scape/Server:master into master
Reviewed-on: #18
2025-03-27 19:36:57 -06:00
45ee87e64e Blast Furnace improvements
Shoving coke no longer requires intense clicking, the player will keep shoveling until the stove is full.
Adding ore that requires coal to the conveyor belt will take the needed coal from the gold satchel, if carried.
2025-03-27 19:30:06 -06:00
Lucid Enigma
bcc3bc069e Potentially fixed bank visuals when opening bank 2025-03-25 20:58:27 +11:00
Player Name
48bc0ffb28 Fixed requirement check issue with PoH portals checking for Plague City completion 2025-03-25 20:55:37 +11:00
DeadlyGenga
7b1ebd4608 Added melee attack animation to Mystic Mud staff 2025-03-25 20:52:15 +11:00
Alex Page
1b0a7d5fca Fixed random events returning players to Lumbridge 2025-03-25 20:51:47 +11:00
sirdabalot
fc2247f457 Fixed teleport on logout for pest control landers 2025-03-25 20:49:09 +11:00
Alex Page
47ac5e93d6 Fixed issues causing pie crafting to stop or consume additional ingredients 2025-03-25 20:48:27 +11:00
Lucid Enigma
63f8ec3cbf Fixed facial expressions in dialogue for Tree Gnome Village NPCs 2025-03-25 20:37:27 +11:00
Player Name
0622aed00c Fixed incorrect items in circulation
Corrected incorrect rune kite (h)
Corrected incorrect adamant kite (h)
Corrected incorrect lamps
Corrected incorrect blurberry specials
2025-03-25 20:28:15 +11:00
4799069ebd Summoning rework
Pouches are not consumed. Points are not lost when summoning. However, lifespan is directly tied to summoning points, and the familiar despawns at zero points.
Created a new pouch storage that can be accessed from the summoning tab by pressing "call pet" when no familiar is active. This allows freely switching familiars as long as points are maintained.
Special move points start at empty instead of full, and replenish 1 point every 3 ticks instead of 15 points every 50 ticks.
2025-03-22 21:23:28 -06:00
4991b07ff8 Changes to Gold Satchel
Only coal will be used from the satchel when smelting, the other ores must be in the inventory (to prevent smelting more than you can carry).
Bars and gems (cut and uncut) can now also be stored in the satchel.
2025-03-21 14:59:52 -06:00
7be39c73b9 All satchels now autoloot items they can hold
The plain satchel, due to its ability to carry any item, will only loot items that already exist in the satchel.
2025-03-17 15:48:40 -06:00
a5eb1ea929 Elemental Battlestaves can optionally be crafted with a Dramen staff instead of a Battlestaff 2025-03-17 10:42:24 -06:00
d02b16d970 Selecting "Make 10" on crafting dragonhide gear now makes 30. 2025-03-17 10:19:17 -06:00
07d8bb6a13 Silver Sickle custom bonecrusher now also "buries" ashes that are dropped by enemies. 2025-03-17 08:53:29 -06:00
61f5120cfc Fixed attack range when attacking large creatures from certain angles
When attacking a 2x2 or larger creature from the SW, attack range would be reduced. Most noticeable on halberds. Fixed by checking the closest tile when attacking from a distance.
2025-03-17 08:24:43 -06:00
fb0dd3a27d Minor fixes for the custom runecrafting implementation
Fixed the exp rate for mind and fire runes.
Combination runes now also get double exp like the rest of runecrafting, and all of them give the same exp as lava runes
Added a message when crafting combination runes without a binding necklace.
2025-03-13 11:19:56 -06:00
81730af934 Customized world list info 2025-03-12 12:04:53 -06:00
6458a6ef95 Satchel changes. Rune satchel allows all runes in Boosted mode. Red satchel allows clue scrolls. 2025-03-12 11:30:37 -06:00
b109b98247 Strange teleorb custom teleport item now only requires 2 Law runes per charge instead of 20 2025-03-12 09:52:19 -06:00
daf312ce6b Fixes related to ironman mode and exp rates
Default exp rate is now 1.0, Restored the option to select 10x (no longer limited to hardcore ironmen)
Restored option to select hardcore ironman mode.
Permadeath now properly clears the custom satchels.
Ironmen can no longer trade (custom feature allow untradebale items to be traded was allowing ironmen to trade all items with a warning)
Implemented option to select Boosted mode
2025-03-12 08:31:22 -06:00
Player Name
dda80d535b Fixed a pet growth bug that could cause an error logging in 2025-03-10 14:18:27 +11:00
9c8c2ada70 Can now talk to people on Lunar Isle even if your seal of passage is in the bank 2025-03-08 15:00:08 -07:00
18d30a3e1e Added ability to swap spellbooks by operating the Mage's Book
The spellbook needs to first be unlocked by completing the quest and using the book on the respoctive altar.
2025-03-08 14:50:09 -07:00
9926b5c244 Fixed autocast bug - Player autocast spell now loads properly on login
This was caused by items being unequipped and re-equipped on login to verify that the player meets the requirements to use it. As a consequence of this bugfix, if the requirements for an item change (like a new quest comes out) then the item won't be forcibly unequipped. I can live with that though.
2025-03-06 21:40:20 -07:00
40d5478b57 Runecrafting rebalance - use one essence at a time
Runecraft with one essence at a time instead of the whole inventory at once. Doubled rune and exp output to compensate.
Also, Ourania and Combination runes are now affected by rune multiplier.
All elemental runes share the same level, exp, and multiplier as air runes.
Add small multiplier for Soul Runes.
2025-03-06 05:57:40 -07:00
randy
5eddd5e98a Merge pull request 'Merge 2009scape updates' (#17) from 2009Scape/Server:master into master
Reviewed-on: #17
2025-03-05 19:11:53 -07:00
fe117e6976 Added Fountain of Heroes to main floor of the Heroes Guild 2025-03-05 15:57:02 -07:00
c59e13f0aa Added Spirit Weed Seeds to Master Farmer drop table 2025-03-05 15:50:40 -07:00
e88bdf94d3 Port Phasmatys entry fee can be paid with Ecto-tokens in the bank 2025-03-05 15:15:24 -07:00
a411c7a5e2 Custom Silver Sickle bonecrusher functionality improvements
The automatic bone burying function now works when the sickle is equipped or in the inventory, and also affects bones from hunter traps.
2025-03-05 11:21:34 -07:00
16da0ed93e Implemented custom satchels
These satchels are normally from creature creation. The custom versions can be crafted by using an enchantment spell on hides, and have a stackable inventory that only stores certain items.
- Plain: Level 1 enchant on Blue D hide. Carries anything but can only be emptied by using it on a bank booth.
- Green: Level 2 enchant on Green D hide. Carries any seed or herb. Harvesting farming plots with the satchel equipped returns noted produce.
- Red: Level 3 enchant on Red D hide. Carries summoning charms. Charms dropped by mobs are automatically added to the satchel.
- Gold: Level 4 enchant on Dagannoth hide. Carries ore. Smelting at the furnace will use ore in the satchel.
- Rune: Level 5 enchant on Suqah hide. Carries catalytic runes. Spells will use runes in the satchel.
- Black: Level 6 enchant on Black D hide. Placeholder, as I don't have a use for it yet.
2025-03-05 09:34:48 -07:00
249c501184 Squashed commit of the following:
commit 3177468c3261b783bdab2486e939121d7d28bff3
Author: randy <randy@in.snowlab.cc>
Date:   Mon Feb 24 20:35:53 2025 -0700

    Final touches on teleorb

commit e7b41fbf509150ef489ab2644e55b0d5baaec638
Author: randy <randy@in.snowlab.cc>
Date:   Mon Feb 24 19:22:04 2025 -0700

    Added new teleorb file

commit f99da5b28817ab5b03fe0506f0e9442d463381a8
Author: randy <randy@in.snowlab.cc>
Date:   Mon Feb 24 19:20:56 2025 -0700

    Implemented Strange Teleorb as a custom teleport item

Implemented Strange Teleport as a custom teleport item
This item is crafting by casting Charge Air Orb on the Abyssal Rift at the center of the abyss. It can be inspected to save a location and add charges, and can be activated to teleport to the saved location. Charges require 20 law runes or an additional orb.
2025-02-24 20:44:17 -07:00
Kennynes
448f970c10 Fixed wrong door used in heroes' quest 2025-02-23 13:13:47 +00:00
Oven Bread
83b8689b86 Corrected quizmaster lamp reward 2025-02-23 04:02:11 +00:00
randy
7592372dc4 Merge pull request 'Update master with 2009scape updates' (#16) from dev into master
Reviewed-on: #16
2025-02-20 20:49:37 -07:00
randy
9b8ba3b6dd Merge branch 'master' into dev 2025-02-20 20:47:03 -07:00
680a08ea98 Merged updates from 2009scape 2025-02-20 19:43:05 -07:00
Oven Bread
6362eee753 Can now recover your digsite trowel from the examiner 2025-02-20 10:53:59 +00:00
Oven Bread
f0ee476e42 Fixed pyramid spawns not despawning
Relaxed getting thrown out of the pyramid to rates that appear more authentic
2025-02-19 07:33:42 +00:00
Player Name
0e65f20223 Fixed another softlock in The Fremennik Trials 2025-02-19 07:31:29 +00:00
Player Name
af58fae1fc Corrected Biohazard requirements check when using West Ardougne's eastern gates 2025-02-19 07:30:51 +00:00
Oven Bread
74ed5e1bee Fixed bug where swamp tar interactions could steal an entire stack 2025-02-19 07:29:26 +00:00
Player Name
cf08e700fb Runecrafting pouches now synchronise across bank instances
BoB can no longer be used to smuggle weapons to Entrana
2025-02-18 06:24:15 +00:00
Player Name
d65a53b275 Fixed loss of equippable items when swapped with another item that is dropped in the same tick 2025-02-18 06:03:18 +00:00
Player Name
fb9d6307b3 Corrected diamond bolts (e) effect
Removed inauthentic Verac armour effect of +1 max hit increase
2025-02-18 04:32:21 +00:00
Player Name
45fb89ec73 Hostile random events now take the player's summoning level into account
Random event combat level is now selected authentically resulting in more difficult hostile random events
2025-02-17 10:44:44 +00:00
Oven Bread
50ba3682b5 Corrected fishing contest quest log
Fixed dialogue after fishing contest
2025-02-17 10:42:19 +00:00
Oven Bread
df73f4a171 Fixed child animations for the following NPCs:
Boy (Witch's House quest)
Kanel (Inside Gertrude house)
Philop (Inside Gertrude house)
Shilop (Gertrude's Cat quest)
Wilough (Gertrude's Cat quest)
2025-02-17 10:40:27 +00:00
GregF
9eedfc2336 Added some new regions 2025-02-17 10:37:28 +00:00
Ryan
e25780a4c5 Fixed player status SQLite conversion breaking on usernames with spaces 2025-02-16 11:10:18 +00:00
GregF
b6560c0e3f Implemented Mogre miniquest 2025-02-16 10:42:27 +00:00
Player Name
2d1a626df0 Zamorak robe top and bottom now count as Zamorak god items
Zamorak robe top and bottom now allowed into Entrana
Implemented the right-click take-boat options for the monks of Entrana
Fixed some silly typos in the Monk-of-Entrana dialogue
Removed unused god item definitions
2025-02-16 10:23:21 +00:00
GregF
4f97dbef8c Completed very large refactor of Plague City quest and related functionality 2025-02-16 10:19:20 +00:00
Player Name
be76ca143b Fixed softlock in Priest in Peril and The Fremennik Trials 2025-02-16 10:03:20 +00:00
Elbarto 2
053254b504 Corrected restore for various consumables 2025-02-16 09:48:21 +00:00
Oven Bread
486ca4bb19 Implemented Desert Treasure quest 2025-02-16 09:33:56 +00:00
DeadlyGenga
104533c11d Corrected stats, levels and combat styles of many familiars 2025-02-16 07:40:42 +00:00
Oven Bread
9886049429 Implemented Zogre Flesh Eaters quest 2025-02-16 07:09:36 +00:00
Player Name
f1131b7d00 Made random event teleports more robust, fixing edge cases 2025-02-16 07:00:13 +00:00
Player Name
be47c1d5c9 Added cats raised to ::stats
Added food cooked to ::stats
2025-02-16 05:35:27 +00:00
Player Name
bb860b60e0 Pest control XP formula is now authentic, includes 1% XP bonus if handing in 10 points or more and 10% XP bonus if handing in 100 points or more 2025-02-16 05:32:57 +00:00
Oven Bread
fed309a70b Fixed map areas that caused client crashes in HD 2025-02-16 05:28:53 +00:00
Player Name
1afd4d5328 Fixed bug preventing The Fremennik Trials lyre concert being marked as completed 2025-02-16 05:22:36 +00:00
Player Name
a6eb706358 Corrected many examine texts 2025-02-16 05:20:14 +00:00
Oven Bread
957477b9b0 Fixed Death Plateau softlock in Dunstan dialogue
Fixed Harold not accepting blurberry special
Spiked boots now accessible
2025-02-16 05:17:13 +00:00
Oven Bread
4387b346dc Implemented The Curse of Zaros Miniquest 2025-02-13 12:34:23 +00:00
Player Name
618d39d73c Fixed shooting star resetting on server shutdown 2025-02-13 11:30:02 +00:00
sirdabalot
62a7dd4324 Ported kill stats and rare item drop storage to sqlite from json
This fixes lengthy and memory intensive server shutdowns
Existing global_kill_stats.json will be imported on first run
2025-02-12 12:17:02 +00:00
GregF
212ce57580 Fixed barbarian fishing xp rate manipulation 2025-02-12 11:35:17 +00:00
84951244f9 Farming leprechauns now give free buckets
There is no way to increase the limit past 31, but buckets are so cheap they are just an inconvenience. So now you can deposit and withraw any number of buckets, regardless of what is stored.
2025-02-11 11:12:39 -07:00
randy
389c5858d0 Reverting sliding scale exp multiplier
Reverting manually instead of doing a git revert because this version of gitea has an error when attempting reverts.

I still like how it reduced the early game boost, however it has some disadvantages. It's impossible to calculate the needed number of actions for a certain level, quest and minigame rewards get more valuable the longer you put them off, and some skills have no early game content and are a bit of a slog. Setting it back to a static multiplier.
2025-02-11 10:35:10 -07:00
randy
5540febf6e Reverting sliding scale exp multiplier
Reverting manually instead of doing a git revert because this version of gitea has an error when attempting reverts.

I still like how it reduced the early game boost, however it has some disadvantages. It's impossible to calculate the needed number of actions for a certain level, quest and minigame rewards get more valuable the longer you put them off, and some skills have no early game content and are a bit of a slog. Setting it back to a static multiplier.
2025-02-11 10:23:06 -07:00
Player Name
8a4ea5d1f3 Runecrafting skillcape now only consume a charge if the teleport took place
Improved navigation of the runecrafting skillcape teleport menu
Runecrafting skillcape message improvement
2025-02-11 13:17:20 +00:00
MrKingFish
0f14639e9b Added examine text for Ahad, Phingspet and Grimesquit 2025-02-11 13:13:31 +00:00
Player Name
e63c6f523a Fixed bug where ::forcegravedeath could make admins lose admin status
Added ::makeadmin command to promote accounts to admin
Added ::dropadmin command to demote accounts from admin
Added ::setpestpoints to set Pest Control points
Testing commands now take optional player name argument ::max, ::noobme, ::setlevel and ::addxp
2025-02-11 13:06:49 +00:00
Oven Bread
612ccc6776 Implemented Maze random event 2025-02-11 13:03:30 +00:00
Oven Bread
b5784c5782 Implemented Quiz Master random event 2025-02-11 12:59:25 +00:00
Oven Bread
867c332466 Populated Gnome Stronghold area 2025-02-11 12:50:41 +00:00
Player Name
8b1763c3ba Improved inventory handling for Jossik 2025-02-11 12:47:06 +00:00
Elbarto 2
d0fdf08d13 Added Plant Cure, Compost and Supercompost to the ::farmkit admin command 2025-02-11 12:18:58 +00:00
8e822234fb Implemented shared banks aka clan banks
Players can talk to a banker to enable clan banks. When enabled, any banking function will use the primary bank of the owner of the clan chat the player is in. This only works while the bank owner is online; if they are offline the player will fallback to their own bank.
2025-02-08 19:46:04 -07:00
718d7e39e1 Cracking safes the the Rogue's Den now repeats
The repetition will stop if you move away, get low health, or run out of inventory space.
2025-02-08 19:46:04 -07:00
Player Name
d91f77c294 Refactored how quests are referred to internally
Fixed numerous requirement checks for Rag and Bone Man II, The Lost Tribe, The Tourist Trap, Waterfall Quest and The Fremennik Trials
2025-02-09 02:37:14 +00:00
7b5ac708dc Implemented shared banks aka clan banks
Players can talk to a banker to enable clan banks. When enabled, any banking function will use the primary bank of the owner of the clan chat the player is in. This only works while the bank owner is online; if they are offline the player will fallback to their own bank.
2025-02-07 16:40:10 -07:00
dd35e479c1 Cracking safes the the Rogue's Den now repeats
The repetition will stop if you move away, get low health, or run out of inventory space.
2025-02-05 21:32:27 -07:00
Player Name
c03947e0b0 Cleaned up legacy save code 2025-02-03 00:48:51 +00:00
Oven Bread
acc6c182a5 Implemented Tears of Guthix Quest
Implemented Tears of Guthix Minigame
2025-02-01 14:20:43 +00:00
Player Name
1b85808885 Fixed hostile random events attacking other players
Fixed players being able to attack hostile random events for other players
Hostile random events now authentically only reward 1/16th xp (except for pheasants, which give 0 xp)
2025-02-01 14:15:45 +00:00
Player Name
2cf8392691 Fixed incubator appearing to lose contents 2025-02-01 14:13:26 +00:00
Player Name
4a016d45e5 Fixed farming patches wrongly getting diseased during offline catchup 2025-02-01 14:08:55 +00:00
Player Name
cd43f8d269 Star sprite bonus now drops bonus ore if inventory is full
Rocks mined attribute now takes into account bonus ore
2025-02-01 14:07:03 +00:00
troido
0a26d5039a Aggressive NPCs will no longer always attack the last player to enter the area 2025-02-01 14:04:03 +00:00
Player Name
c3929cf06c Fixed telegrab through walls 2025-02-01 14:00:59 +00:00
Player Name
fcbea5acdc Fixed some incorrect replacement doors in Rellekka
Disabled the unimplemented TzHaar door
Fixed some doors causing buggy player movement
2025-02-01 13:58:55 +00:00
DeadlyGenga
3d7e93b6c2 Refactored Antifire potion effect
Relicym Balm can now cure disease
Antifire potion effect now persists through log out
2025-02-01 13:53:10 +00:00
Ceikry
6e9f3cb8b9 Disabled the ability to create new HCIM accounts
Disabled the ability to select HCIM 10x xp rate
Existing HCIM will turn back to standard players after their next death (all permadeath rules still apply for that death)
Players on HCIM 10x will be reverted to 5x after their next death
If the player is in combat and disconnects via AFK timeout, then their forced logout timer has been reduced to 30 seconds from 15 minutes
If the player is in combat and x-logs or disconnects via network issues, then their forced logout timer has been reduced to 5 minutes from 15 minutes
2025-02-01 13:48:18 +00:00
GregF
0c8efbac7a Added all missing NPCs to config file for future use 2025-02-01 13:42:53 +00:00
Tooze
ccc60b1240 Implemented knife spawn in Sorcerer's Tower 2025-02-01 13:38:50 +00:00
Player Name
818ffc01e7 Removed random events from Tutorial Island 2025-02-01 13:27:04 +00:00
GregF
f37b30749e Iron men can no longer participate in lootshare 2025-02-01 13:25:23 +00:00
Player Name
edc6f9cf07 Implemented Gaze of Saradomin
Fixed respawn bugs
2025-02-01 13:23:42 +00:00
DeadlyGenga
064edfbbe4 Implemented spirit saratrice 2025-02-01 13:20:27 +00:00
GregF
b62f4e9525 Implemented black swan NPC 2025-02-01 13:19:17 +00:00
Player Name
57f5617fe8 Fixed loss of draconic visages if used on an anvil without an anti-dragon shield in the player's inventory
Fixed dragonfire shield and dragon square shield smithing not checking for a hammer in inventory
2025-02-01 13:18:19 +00:00
GregF
6c386e6ed2 Implemented Hunters' Crossbow 2025-02-01 13:15:07 +00:00
GregF
93890769e1 Significantly improved slayer tasks implementation
More authentic tasks
Better dialogue and hints
New tasks added in preparation for when relevant quests are implemented
2025-02-01 13:14:04 +00:00
Oven Bread
4039c0123b Correctly limited quest log and achievement diary scrolling 2025-02-01 13:12:58 +00:00
Player Name
27f2d457ea Fixed issues in Jarvald's dialogue 2025-02-01 13:08:20 +00:00
Oven Bread
c6b508b3ed Lucien now gives a pendant after Temple of Ikov completion 2025-02-01 13:07:49 +00:00
Oven Bread
67b6855370 Implemented Sea Slug quest 2025-02-01 13:07:39 +00:00
GregF
4480618748 Added pirate clothes to store and examine text to Mike 2025-02-01 13:06:53 +00:00
Player Name
9fff4dbb5d Implemented spade pickup in the Rimmington mine
Rewrote the permadeath code to more thoroughly wipe HCIM when they die
HCIM permadeath code destroys items dropped on death
Cleaned up redundant blast furnace player save code
2025-02-01 13:02:22 +00:00
GregF
6a68cf9d7c Fixed issue with trees not being able to be health checked 2025-02-01 12:56:18 +00:00
Oven Bread
e34a81ec6c Implemented Heroes Quest 2025-02-01 12:29:51 +00:00
Oven Bread
00b5a44c31 Added additional dialogue helpers 2025-02-01 12:23:10 +00:00
Player Name
f75577d41d ::itemsearch now usable by everyone 2025-01-20 13:56:18 +00:00
Player Name
3838f01adf Made POH deaths safer, fixing random event bug 2025-01-20 13:54:34 +00:00
Player Name
a932c309b3 Fixed bug where ultimate ironmen could have rewards sent to bank
Fixed the allquest command on new accounts on first login
2025-01-20 13:52:46 +00:00
Player Name
9af776e3c6 Refactored some runecrafting code
Corrected the Ourania altar reward, now fully authentic
2025-01-20 13:49:58 +00:00
gregf36665
39634b6caf Implement penguin egg acquisition 2025-01-20 13:26:56 +00:00
Oven Bread
eb3884180e Signpost improvements
Minor quest log fixes
Implemented map for talismans in Wizard Tower basement
2025-01-20 12:56:20 +00:00
Player Name
d5c7d74767 Fixed a bug where the player could get locked planting while farming 2025-01-20 12:46:57 +00:00
Player Name
d08656021c Fixed pillory random event completion teleport location 2025-01-20 12:35:01 +00:00
Kennynes
9beb7219ea Fixed examine texts for some cabbages and other items 2025-01-20 12:25:25 +00:00
Player Name
deecf136bc Fixed slayer cape perk activation 2025-01-20 12:08:58 +00:00
Player Name
b849de2dab Fixed magic secateurs patch bones not applying 2025-01-20 12:00:38 +00:00
GregF
b4950343ec Fixed bug preventing super restore recovering prayer 2025-01-20 11:49:01 +00:00
6cf3e97985 Merge pull request 'Implemented familiar auto loot blocklist' (#10) from dev into master
Reviewed-on: #10
2024-12-08 14:46:37 -07:00
579df71848 Implemented familiar auto loot blocklist
Adding the name of an item to the player ignore list will prevent familiars from looting that item.
2024-12-08 14:36:34 -07:00
bd8669f629 Merge pull request 'dev' (#9) from dev into master
Reviewed-on: #9
2024-12-07 20:23:38 -07:00
97053798ff Fixed free spell cast bug and removed delay on bind spells
The chance to not consume runes was being checked before checking if the runes were in the inventory, allowing casting of spells that the player did not have the runes for. This has been fixed. Additionaly, the forced delay on bind spells has been removed, so they can be spammed. This is definitely unbalanced for pvp, nobody does pvp so it's fine. If possible in the future, reinstat the delay when targeting players.
2024-12-07 20:16:45 -07:00
b1ca068e3f Removed delay on note spell casting
Since it only does 10 items at a time, it is often cast 2-3 times in a row. The delay is unnecessary for this spell.
2024-12-07 19:54:27 -07:00
05c1ac1dc0 Added runecrafting exp when crafting altar teleport tablets 2024-12-07 19:36:12 -07:00
b381d78362 Merge pull request 'dev' (#8) from dev into master
Reviewed-on: #8
2024-12-06 09:17:07 -07:00
ed9a6fa118 Merge branch 'master' into dev 2024-12-06 09:16:27 -07:00
ecb3a66d58 Fishing with Hunter feathers and Note Spell changes
Feathers earned with hunting can now be used for fly fishing. The note spell no longer charges a 10% tax, but is instead limited to noting 10 items at a time.
2024-12-06 09:13:09 -07:00
ffe1e11d11 Traps with a catch now last 10 minutes instead of 1 2024-12-06 08:05:40 -07:00
ff80ec3b51 Merge pull request 'Update Master' (#7) from dev into master
Reviewed-on: #7
2024-12-05 15:59:23 -07:00
be7143be20 Updated pickpocket to stop if health gets low
If your health is low enough that you'll die if you get caught, then pickpocketing will stop repeating.
2024-12-05 15:53:31 -07:00
a788b64e44 Added Burgh de Rott bank functionality 2024-12-05 15:20:10 -07:00
6cc6a4c69c Fixing minecarts that lead to keldagrim. 2024-12-05 15:01:26 -07:00
30113613e0 Merge pull request 'Update Master' (#6) from dev into master
Reviewed-on: #6
2024-11-24 22:03:59 -07:00
40196333ad Changes to Ava's devices from Animal Magnetism
The devices no longer need to be worn, completing the quest is enough. Also standardized arrow break rates to exactly 20% at all levels. Ava's devices will pick up 100% of the arrows that don't break.
2024-11-24 21:52:06 -07:00
f3c9dd2d9c Changed Low Alch spell into Note Spell
Casting on an item will note all of the matching items in your inventory. 10% of the items (rounded down) are charged as a "tax" so that other remote deposit options are still competitive.
2024-11-24 21:07:42 -07:00
f258632fb5 Farming produce is automatically noted if wearing an amulet of farming or amulet of nature
Since the leprechaun is right there and can do the same function, this just saves the annoying clicking of harvesting and using the produce on the leprechaun over and over.

Note that the other functions of the amulet of farming or amulet of nature are not implemented yet.
2024-11-24 20:21:34 -07:00
Oven Bread
7749e8263f Introduced new dialogue engine compatible with spinoff revision 578 project 2024-11-22 06:59:42 +00:00
e19c89f441 Merge pull request 'Update master' (#5) from dev into master
Reviewed-on: #5
2024-11-21 17:45:09 -07:00
3119a01e3f Additional steps to remove random events
The random events came back for some characters, seems the timer got started somehow. This change should pause the timer every time it's registered, so it should never start again.
2024-11-21 17:44:13 -07:00
fba2e413cf Added small summoning exp gain every minute a familiar is summoned
The gain is the summoning level required + 10.
2024-11-21 17:10:08 -07:00
456925264b Merge pull request 'Push updates to master' (#4) from dev into master
Reviewed-on: #4
2024-11-21 06:43:18 -07:00
6ed0ead8a7 Removed door dialogue in the stronghold of security
No need to explain how to secure your account over and over, we aren't kids.
2024-11-20 19:39:50 -07:00
6cca2d1bed Allow trading any item
Attempting to trade an untradeable item will give a warning message, but the trade will still be allowed to go through.
2024-11-20 19:33:24 -07:00
440f8ce3ac Corrected default exp rate
Changing the default exp rate to anything other than 5.0 causes the new rate to override the player's selection at tutorial island. I lowered the default just to reduce the amount of exp you gain during tutorial island, but with the new scaling exp rate that's no longer required.
2024-11-20 19:21:17 -07:00
ee11daa0be Merge pull request 'Refresh dev environment' (#3) from master into dev
Reviewed-on: #3
2024-11-17 17:16:58 -07:00
b33ad22740 Removing Jarvald reference to NPC Constlib, since I have no idea how to update that
The original change (fb2535e248) Says it requires a constlib update. From what I can tell, that is made here: https://gitlab.com/2009scape/tools/rs09-constants-library.

However, I have no idea how to actually get a new build of this. Perhaps the server files will be updated to include this in the future, but in the meantime I am changing this back so the server actually starts.
2024-11-17 17:16:20 -07:00
3f8828457b Merge pull request 'Initial build of Snowscape' (#1) from dev into master
Reviewed-on: #1
2024-11-17 15:51:17 -07:00
793acecf2e Merge pull request 'Updating from 2009scape' (#2) from 2009Scape/Server:master into master
Reviewed-on: #2
2024-11-17 15:45:30 -07:00
eebcb25274 Updated fletching message to include the new arrowshaft quantities 2024-11-17 14:18:44 -07:00
9ed560cca6 Can now fletch arrow shafts from higher tier logs, getting more arrowshafts in return
This uses the same exp, level requirements, and arrowshaft output as Old School Runescape.
2024-11-17 14:16:39 -07:00
a86b734e0d Replaced "Make 10" with "Make 30" for crafting interfaces that use the chat dialogue
The client will still display 10, but the action will repeat 30 times.
2024-11-17 13:04:51 -07:00
5319cabdae Selecting "make 10 bars" in the smelting interface will now make 30, allowing a full inventory 2024-11-17 12:59:35 -07:00
ee6f83eb7e Improved timing of pickpocket repeating, and allowed repeating even when caught 2024-11-17 10:56:19 -07:00
63c7fc3f70 Fixed typo in variable name 2024-11-16 16:45:43 -07:00
8f55baa13e Experience multiplier now scales based on the skill level
This slows down the extremely boosted early game while still allowing the multiplier to reduce late-game grind.
2024-11-16 16:19:06 -07:00
6845a3740f Rebalanced prayer point drain rates (almost everything except protection prayers drains far less)
- Protection prayers are now all equal at 10 points per minute
- the 5%, 10%, and 15% bonus prayers now drain 1, 2, and 3 points per minute, respectively. Having all of them active at once is now a lower drain than Chivalry.
-- The attack and strength versions drain half of that, since you need two of them active.
- Chivalry and Piety now also boost magic and ranged at the same rate as attack, and have had their drain slightly lowered. (7.5 and 10 points per minute)
2024-11-16 09:47:46 -07:00
02a36f7bf7 Removed gloves of silence requirement for auto-pickpocketing 2024-11-15 12:37:47 -07:00
Ryan
79c69e3d43 Changed timestamp in logs to ISO standard datetime format 2024-11-14 12:42:48 +00:00
GregF
7d79c9a82a Construction can now be used to fix the ladder to the wilderness beacon 2024-11-14 12:14:31 +00:00
Oven Bread
e216a6366f Use the system update countdown for the daily restart 2024-11-14 12:08:21 +00:00
Player Name
9054e36288 King Bolren now gives back lost gnome amulets 2024-11-14 12:05:47 +00:00
Oven Bread
1a67932351 Implemented Pillory random event (this event occurs while pick-pocketing) 2024-11-14 12:02:37 +00:00
Player Name
7a6adda997 Authenticity improvements to runecrafting pouches. Due to changes in degrade counters, they will degrade within the next 1 or 2 fills after this update; this is a one-time event. Resolve (or prevent) this by repairing via the dark mage 2024-11-14 11:56:16 +00:00
GregF
1da53c448d Rewrote dream spell
Fixed healing rate of dream
Added sound to dream spell
2024-11-14 11:51:34 +00:00
Player Name
53dc169774 Corporeal beast authenticity improvements
Dark core will no longer jump to players in safe area
Adjusted maximum attack hits
Protect from magic now blocks the big dart attack by 40%
Now resummons the dark core when it dies
Made the dark core respawn mechanics more authentic
Dark core now drops ashes
2024-11-14 11:36:09 +00:00
Player Name
b0be48501b Made the following POH hotspots refund their non-plank items when torn down: any quest item, any guild trophy, any armor stand
Refactored some construction code
Removed decorations and hotspots for "Menagerie" rooms, which seem to be inauthentic
Changed the following hotspots to be recursive: decoration, wall chart (study)
Corrected xp for a few construction items
2024-11-14 11:18:25 +00:00
Player Name
98c99e7550 POH telescope now estimates the shooting star time based on its tier 2024-11-14 11:06:41 +00:00
Player Name
ed41f3a228 Bounty Hunter music is now unlockable 2024-11-14 11:02:26 +00:00
Player Name
a2eafa8bc0 Fixed exception on server boot related to a duplicated attempt to register dialogue for Jarvald 2024-11-14 10:44:00 +00:00
Sinipelto
2be5f9eac0 Fixed loss of combat tabs after recruitment drive 2024-11-14 10:22:04 +00:00
251934d3a9 Audded autolooting of coins and tokkul if wearing ring of wealth 2024-11-13 20:51:24 -07:00
964c95b572 Fixed noncombat spells getting the 33% chance to not use runes. 2024-11-13 15:21:17 -07:00
e7ed5b0d7a Added check to allow permanant unlock to act as a worn ring of wealth
This only affects NPC drops, not gem drops from mining.

Due to this affecting the rare drop table only, this has not been tested.
2024-11-13 14:23:17 -07:00
a6ad4f8f3b High Alching Ring of Wealth now adds permanent unlock. 2024-11-13 14:10:24 -07:00
0b05424690 Changed Imp-in-a-box to act a full deposit box. One-time use.
The Imp-in-a-box is broken, and I don't know how to fix it so I buffed it instead.
2024-11-12 21:04:11 -07:00
e45f8fa81b Implemented rune multipliers on combination runes
The altar you are at will now give its multiplier to the combination runes crafted. This only saves essence, the opposing rune cost is multiplied as well. (If you get 10 air runes per essence, then making mist runes at the air altar will cost 10 water runes and 1 essence to give you 10 mist runes).
2024-11-12 16:00:02 -07:00
3035aaa7e6 Implemented proper probabilities when using the Ourania altar 2024-11-12 15:00:37 -07:00
9143cd5f47 Fixed getting slayer exp from hunting 2024-11-12 07:52:06 -07:00
d92a3f3a8e Increase bird snare feather drops from 8 to 60
As part of the plan to make self-sufficient fletching more viable.
2024-11-12 06:24:27 -07:00
65cb778a18 Hunter traps last 10 minutes instead of 1 minute 2024-11-11 22:16:17 -07:00
563fcc23bc Farming patches now return the seeds if the plant being cleared or harvested is fully grown 2024-11-11 21:43:20 -07:00
2e68ac1522 Fixed overwritten herb cleaning line 2024-11-11 19:16:53 -07:00
62463576cf Herb cleaning is now a pulse that cleans all the matching herbs in your inventory 2024-11-11 19:14:58 -07:00
ac14e7a22e Remove last check preventing Ancient Magicks from working in wilderness. 2024-11-11 18:29:55 -07:00
f2aad1c957 Allow Ancient Teleports to bypass the isTeleBlocked function so that they work in the wilderness
Note that they are still restricted by the checks in the ZoneMonitor file, which does include teleblock checks, as well as specific wilderness levels.
2024-11-11 18:05:16 -07:00
ee3a71a5d0 Allow Ancient Magick spells to work up to level 48 wilderness. 2024-11-11 18:01:57 -07:00
root
a361915281 Familiar timer now only decreases if at zero summoning points
This change reduces the upkeep cost of Summoning, now you only need points or pouches, but not both.
2024-11-11 09:57:51 -07:00
e5e6006212 Protect from Summoning prayer now prevents NPC aggression when active 2024-11-10 22:36:00 -07:00
fa808accd4 Added requirement checks to the teleport tablets 2024-11-10 22:09:13 -07:00
df3fc6c973 Increased the Herb:Tar ratio from 1:15 to 1:45
Salamanders burn through ammo, and herbs are a lot of work to come by. This increases how much ammo you get per herb by 3x.
2024-11-10 21:17:30 -07:00
da071b795d Normal trees now have a chance to deplete, like higher level trees
This is done to make collecting normal logs for arrowshafts less click-intensive.
2024-11-10 20:55:02 -07:00
48069e2234 Added Lunar Robes to Moonclan clothing shop
There is no way to get the robes until the quest is implemented, so this is a temporary solution. The robes are very cheap, but they also aren't very good so it should be fine.
2024-11-10 20:47:32 -07:00
2e65f0d186 Changed combat spells to only consume runes 33% of the time
This is part of the attempt to balance ammo production and consumption rates.
2024-11-10 19:06:13 -07:00
7da14bd3b8 Increased Crystal Equipment charges to 12,000 2024-11-10 18:46:47 -07:00
de4f67832b Changed crystal equipment to recharge with rune arrows and kiteshields instead of coins
New Crystal Bows require 3600 rune arrows, recharging requires 3000. New Shields cost 24 Rune Kite shields, recharging requires 20.
2024-11-10 18:45:01 -07:00
d21452a8bf Increased arrowheads and other ranged ammo produced from bars by 3x
This is done with the intention to balance the value of rune arrows and bolts with the value of a rune bar (12800 if you make high-end rune gear and high alch it), as well as the value of the crystal bow (72 gp per shot).
2024-11-10 17:36:38 -07:00
e8afa4e529 Add damage scaling to Crumble Undead
The spell damage scales like Magic Dart, but due to the limitation of only affecting undead enemies, deals 2 more damage.

If the enemy is not undead, deals only 1 damage (set to 1 instead of 0 to prevent splashing abuse).
2024-11-10 15:15:10 -07:00
98e8cd4592 Fixed incorrect autocast animation for the three god spells
Also future-proofed by adding animation for Iban's blast, even though the staff is not obtainable yet and the spell can't normally be autocast.
2024-11-09 21:47:02 -07:00
974c44a041 Remove staff check when autocasting Slayer Dart and Claws of Guthix
This should have no effect on the slayer dart spell, but does allow Claws of Guthix to be autocast with the Guthix Staff and not just the Void Mace.
2024-11-09 20:29:01 -07:00
325b8f6f20 Crystal Bow and Shield no longer lose stats as they degrade 2024-11-09 15:45:48 -07:00
13e4a0b640 Added code to customize logout timer. Set to 1 hour. 2024-11-09 09:08:05 -07:00
f4c51dbafe Reduced Quest Point Requirements for Culinomancer shop so that all gloves are obtainable 2024-11-08 21:17:27 -07:00
81779f1fae Fixing missing healing on Void Spinner Familiar 2024-11-08 21:12:11 -07:00
7113ef393d Added Swamp Tar to Lumbridge General Store 2024-11-08 17:29:55 -07:00
3fd5f60d7f Increased Abyssal Titan carrying capacity 2024-11-08 16:23:02 -07:00
acfd5c90b9 Increased Abyssal Parasite carrying capacity 2024-11-08 16:22:42 -07:00
6195af3e7b Increased Abyssal Lurker carrying capacity 2024-11-08 16:22:21 -07:00
4eebee34c4 Removed Beast of Burden carrying restrictions 2024-11-08 16:21:10 -07:00
8a72ac52db Changed Summoning shard cost for all pouches to 1
Shards are a pure gold sink, which we don't need.
2024-11-08 16:09:13 -07:00
7779d03654 Added Beast of Burden Looting
Mob drops will go to a beast of burden inventory if there is room.

The Pack Yak, if it has Winter Storage scrolls in its inventory, will instead send drops straight to the bank at the cost of one scroll.
2024-11-08 15:42:16 -07:00
1f538d2976 Added agility exp gain when running 2024-11-07 15:56:58 -07:00
4b5687b8ab Added small slayer exp drop even when not on slayer task 2024-11-07 11:53:41 -07:00
d4da75e6ab Added bonecrusher functionality
If the blessed silver sickle is in the player inventory (not equipped) and they have enabled the feature by using "cast bloom", bones will not drop and instead the player will directly get the prayer experience.
2024-11-07 10:05:10 -07:00
8094465579 Added ability to toggle bonecrusher function on blessed sickle
When using "cast bloom", the sickle will grow nearby fungus as usual. If no fungus are grown, it will instead toggle the bone crusher attribute.
2024-11-07 10:02:10 -07:00
6305e2a718 Rewrote Alchemy spells to use pulse, repeating up to 30 times 2024-11-06 13:36:24 -07:00
bbaa4bbdbe Fixed exp reward calc and reduced to one fifth
The reward formula double-dipped with exp multipliers. Corrected the formula to match the original 2009 version, then reduced to 1/5 to account for the increased points given for each round.

The increased points are meant to reduce the grind for the items, but not the purchasable exp.
2024-11-06 11:06:23 -07:00
4cd703097c Increased Pest Control reward points by 5x (10, 15, and 25 for each boat) 2024-11-06 11:03:21 -07:00
1ef61d8c33 Reduced Magic Training Arena costs to 1/5th 2024-11-05 20:00:47 -07:00
54d755b12d Enabled quick banking 2024-11-05 19:48:25 -07:00
010cd58460 Updated server config to desired settings 2024-11-05 19:29:58 -07:00
9295c91a7d Plank Make now automatically recasts on matching logs 2024-11-04 20:37:26 -07:00
86913bf75f Added additional stats to Monster Examine and removed the coin cost from Plank Make.
Correctly done on the dev branch this time.
2024-11-04 19:42:31 -07:00
90c80fbd8e revert 4912b39831
revert Added additional stats to Monster Examine and removed the coin cost from Plank Make.
2024-11-04 19:40:36 -07:00
4912b39831 Added additional stats to Monster Examine and removed the coin cost from Plank Make. 2024-11-04 19:39:29 -07:00
17ac2ab4da Added Death and Blood Talismans to the drop tables of abyssal creatures 2024-11-04 19:30:28 -07:00
845a00079b Changed Soul Altar to use the Telekinetic Grab tablet instead of the Runecrafting Guild tablet 2024-11-04 19:19:38 -07:00
82681e8303 Changed Runecrafting Guild Teleport tablet to the unused Telekinetic Grab tablet 2024-11-04 19:18:39 -07:00
d74d83b2ec Fixed missing dependency for Soul Rift teleport 2024-11-04 06:13:23 -07:00
2abf540515 Added Runecrafting Guild Teleport tablet (teleports to Soul Altar) 2024-11-03 18:54:51 -07:00
78847c2415 Ourania altar now gives multiple runes per essence, the same as all other altars. 2024-11-03 18:44:48 -07:00
540db8e15a Added ability to craft altar teleport tablets by bringing soft clay to the respective altar. 2024-11-03 18:40:32 -07:00
020c10d961 Placed new Soul Altar on Crandor 2024-11-03 18:34:44 -07:00
0f00221cd9 Updated Soul Rift in Abyss to teleport to new altar on Crandor 2024-11-03 18:32:19 -07:00
bbf8f3c348 Ore rocks now only have a chance to deplete based on Mining Level and rock respawn time 2024-10-31 21:49:05 -06:00
0fddd2134b Firemaking now repeats until all the matching logs in your inventory are used 2024-10-31 15:20:14 -06:00
b35159b5b7 Implement pickpocketing auto-repeating if the player is wearing Gloves of Silence
The player.lock function doesn't run if the player has gloves on, since it prevents triggering the pickpocket action immediately afterwards. To avoid pickpocketing every tick, we check the game ticks if the player has gloves and only perform the action every other tick.

Testing indicates that manual pickpocketing can occur every 2 ticks, so the auto-repeat occurs at the same rate. If the player fails pickpocketing, the stun will prevent the auto-repeat from activating.
2024-10-28 12:54:33 -06:00
490c6d42ec Autocast changes - All God staves can autocast, and autocast is now independent of active spellbook. 2024-10-27 21:06:08 -06:00
bc5958b60d Added -Xmx flag to specify the amount of RAM the server allocates 2024-10-27 20:57:03 -06:00
cc6f33ab7e Updating mining levels for mithril, adamant, and rune to 50, 60, 70. 2024-10-27 20:49:19 -06:00
62dc8bc324 Updated Smelting levels
Lowered Adamant and Runite smelting to 60 and 70 respectively.
2024-10-27 20:42:40 -06:00
ea980fe909 Updating Smithing levels
Each item's smithing level requirement has been lowered to match the level required to smith that bar. Adamant and Rune are lowered to 60 and 70 respectively.
2024-10-27 20:41:11 -06:00
ab78bce769 Extend grave timers 2024-10-27 20:24:20 -06:00
af47ae42c1 Disabled logout timer 2024-10-27 20:22:13 -06:00
661aa22e41 Disable Random events 2024-10-27 20:16:48 -06:00
Oven Bread
876b87b72a Fixed exception occuring when JohnnyBeard dies 2024-10-12 05:34:12 +00:00
Player Name
32cd17bfa2 Fixed bugs relating to player location and random events
Improved handling of random events when the location of the abducted player is missing from save file
Added per-tick auditing of data relating to player location
2024-10-12 04:21:49 +00:00
Ceikry
393752d77b Fixed vinesweeper point exchange ratio bug
Disabled spirit kalphite scroll until bug is fixed
2024-10-11 04:52:08 +00:00
Player Name
2b460b64f2 Thread now correctly disappears every 5 items crafted, including bug fixes related to the fact
Ring of forging now correctly disappears every 140 iron ores smelt, including bug fixes related to the fact
2024-10-10 07:14:48 +00:00
Player Name
59075798ea Adventure bot improvements
Adjusted max pulse count for bots from 50 -> 75
Adjusted max skill baseline by +4 for adventure bots to 69
Added 500 lines of unique
Added 45000 bot names
Added recovery methods for bots that get stuck
Added multiple random number functions to help add more variance
Added function to handle checking if a bot is near/in a Bank
Added function to handle getting a new city
Added function to add variability to locations given to bots
Added function to check if other players/bots are nearby
Added function to handle bots banking their inventories if a bank booth is nearby
Improved random number parameters
Improved how bots are spawned & added variance to spawn location
Improved how bots interact & handle the Grand Exchange location
Improved how bots handle starting in Lumbridge
Fixed bots referencing themselves in dialogue
Fixed bots talking when they are by themselves
Fixed multiple cases where bots would get stuck in a state
Fixed multiple logic errors
Fixed issues with bots banking but not switching states
2024-10-10 07:08:57 +00:00
DeadlyGenga
034c05f512 Fixed Mystic Lava staff not counting as earth runes 2024-10-08 07:19:23 +00:00
Tobias H.
01b5e59250 Fixed missing space in pottery messages 2024-10-08 06:57:51 +00:00
Oven Bread
7d32e77860 Removed unnecessary server console debug prints 2024-10-08 06:39:25 +00:00
Oven Bread
f11149ebe2 Corrected quest log for Creatures of Fenkenstrain
Corrected quest log for Troll Stronghold
Corrected quest log for The Dig Site
Corrected quest log for Witch's House
Corrected quest log for Scorpion Catcher
Corrected quest log for Wolf Whistle
Corrected quest log for Nature Spirit
Corrected quest log for What Lies Below
Populated NPCs in Lucien's Camp (Next to Wildy Chaos Temple hut) (Inaccessible)
Populated NPCs in Black knight catacombs (WGS) (Inaccessible)
Populated NPCs in 1st level of Pollnivneach Slayer Dungeon (Smoking Kills) (Accessible)
2024-10-07 11:30:02 +00:00
Oliver Fawcett
ba1e190cdb Typo fixes for dialogue in enter the abyss and dragon slayer 2024-10-07 11:26:03 +00:00
GregF
1c9fbc79aa Fixed up wordwrap for debugging unhandled interactions 2024-10-07 11:24:01 +00:00
zsrv
218a040f8b Major farming improvements including (but not limited to):
Farming animation corrections
Farming message updates and additions
Gardeners will chop down fully grown trees for 200 gp
Gardeners will give farming advice
Compost bin debugging admin command ::finishbins restored (finishes any in-progress compost bins)
Compost bin debugging admin command ::resetbins added (resets the player's compost bins to their initial states)
Players can no longer pay gardeners to protect diseased or dead farming patches
Can no longer water dead patches
Weeds will now grow in farming patches as part of the offline catch-up
Trees that are not fully grown can now be dug up
2024-10-07 11:15:52 +00:00
Bonesy
cc8dd4edb4 Rewrote some Varrock plugins
Removed duplicated handlers for many Varrock related stairs and ladders
Fixed the Champions' Guild trapdoor being unable to be closed
Changed the model ID for the logs outside of Seth Groats's house when the axe is taken
The drawers in Guidor's house will no longer disappear. There doesn't appear to be a related open object, so they just won't open
The Benny NPC yells about newspapers again
Knocking on the door in the Varrock bank will start the dialogue with the bankers
The Varrock Census object in the Varrock castle will now open the Varrock Census interface
Added sounds for opening/closing drawers, wardrobes, and cupboards and animations for wardrobes
Added the Kudos overlay in the Varrock Museum
Corrected the spawns for archeologists in the Varrock Museum dig site area
Implemented the Varrock museum map interface to open from the objects and item
The tool shelf in the museum dig site area will now give items
Looking at the displays in the in the Natural History area of the museum will open the interface for the exam and provided some notes
2024-10-06 13:12:33 +00:00
Player Name
3982258500 Corrected iron-ore-smelting success rate 2024-10-06 13:06:07 +00:00
Kennynes
2be422e7da Reverted some tutorial island dialogue 2024-10-06 12:49:54 +00:00
Ceikry
5abe50430d Improved bank updates, likely fix for bank UI delay 2024-10-06 12:47:54 +00:00
Oven Bread
97b8ed19a6 Fixed the Priest in Peril items not showing up in HD 2024-10-06 12:45:10 +00:00
GregF
9d38357c4e Corrected Lighthouse Dagannoth drop table 2024-10-06 12:43:22 +00:00
Player Name
64fa1d89c3 Players are now rescued out of the unimplemented high-level Bounty Hunter crater 2024-10-06 12:35:49 +00:00
Player Name
e31397b42f Corrected summoning point drain rate 2024-10-06 12:29:07 +00:00
Doggo
9f61ffb153 Rewrote the swing/attack handler for authenticity:
Fixed a lot of off-by-1 miscalculations
Fixed the void ranger bonus, which should be 20%
Fixed granite maul spec not giving xp and ignoring protection prayers
Fixed accuracy being too high
Fixed set bonuses that boost attack or defence being applied twice or to all attacks
Fixed ranged attacks not taking into account prayers that boost defence or defensive attack styles
Fixed red chinchompa having the same damage as normal chinchompas
Fixed some specs being boosted by offensive prayers
::calcmaxhit now has better formatting
::calc_accuracy renamed to ::calcaccuracy for consistency with other commands, and now also gives the actual hit chance
2024-10-06 12:23:24 +00:00
Player Name
b2f7f86d6a Fixed blessing of graves belonging to ironmen
Fixed POH teleport issue
Fixed charter requirements
Fixed entrana weapon check bypass
Small authenticity improvements
2024-10-06 11:14:56 +00:00
Player Name
4799176c14 Fix bugs with familiars
Fixed a bug where familiars would sometimes not respond to the 'Call' button
Fixed random events not spawning when standing in front of the Lumbridge furnace
Fixed incorrect restrictions on familiars and random events inside POHs
2024-10-06 11:03:33 +00:00
Player Name
0a89439c80 Fixed saving of prayer points & hitpoints, dynamic level is now tracked separately to current hit/prayer points 2024-10-06 10:58:18 +00:00
Player Name
9c202aa47a Rewrote pet back end to use a more authentic system
Pets can now stack
Fixes a bug where a pet could get reset to 0 hunger and growth when dropped
Player save version migration messages are no longer shown
Pets now morph into adults in-place
2024-10-06 10:47:36 +00:00
Ceikry
6b0f942598 Fairy ring refactor
Reimplemented the travel log, fixes the issue where all the travel log interface text collapses in on itself
Travel log now displays the relevant code
Log sorting implemented
Fairy ring now remembers the last entered code and automatically re-enters it when opened
Fairy ring no longer skips letters
Direction clicks in quick succession now turn the wheel multiple times
2024-10-06 10:21:58 +00:00
Oven Bread
8db9060a40 Implemented the ogres in combat training camp 2024-10-06 09:59:03 +00:00
Player Name
f94221918f Fixed the unlocking of region-wide music tracks 2024-10-06 09:32:57 +00:00
DeadlyGenga
cc47f4b488 Fixed cockatrice, godtrice and spirit cobra egg implementation 2024-09-11 07:11:14 +00:00
GregF
5c027e2649 Corrected fletching arrow xp 2024-09-11 07:09:46 +00:00
GregF
4156f28b93 Corrected many potion effects 2024-09-11 07:07:55 +00:00
Oliver Fawcett
c271d4e74b Fixed typo in Straven dialogue 2024-08-25 03:50:33 +00:00
DeadlyGenga
97464ef1d2 Corrected All Fired Up item requirements 2024-08-23 01:29:44 +00:00
A F (Hawk)
518b5d91dd Fixed woad leaf typo in Wyson dialogue 2024-08-22 08:19:08 +00:00
Oven Bread
e25c7d7824 Implemented Recruitment Drive quest 2024-08-21 14:04:44 +00:00
DeadlyGenga
29fa9a5a21 Rune hasta requirements fix 2024-08-21 13:25:11 +00:00
Roderik
083df1aae2 Superheat ore order of precedence has been corrected 2024-08-21 13:13:15 +00:00
Jared Thorne
3a73c98e19 Improved server music config loading logging 2024-08-21 13:08:46 +00:00
Ceikry
80cb2d9ba4 Reverted fix for issue where the player could be given stacks of 0 items as it causes other regressions 2024-08-17 01:41:17 +00:00
Ryan
317b4e9b5c Reverted erroroneous config changes (fixes bots missing on single player) 2024-08-10 13:17:11 +00:00
Roderik
24143f5d20 Crafting guild sink can now be used 2024-08-10 11:27:23 +00:00
GregF
209e1ae8cf Implemented taxidermist 2024-08-10 11:13:56 +00:00
Player Name
37836ae992 Improved lootshare so that drops are awarded to clan members near a slain NPC, participation in combat is not necessary
Reconnecting in combat will no longer cause lost drops
2024-08-10 11:09:43 +00:00
Player Name
d15b47fc65 Rewrote the music system to use regions 2024-08-03 08:00:36 +00:00
Kamja Imo
58d9101ef6 Rewrote Lumbridge swamp hole handling 2024-08-03 07:24:47 +00:00
Player Name
ca8bfeab64 Trimmed crafting cape can now be used to enter the crafting guild 2024-08-03 07:20:38 +00:00
Player Name
957b1c2944 Rewrote (un)holy symbol blessing handling 2024-08-03 07:18:10 +00:00
DeadlyGenga
92fcd0ec32 Corrected granite crab pouch crafting experience from 31.6 to 21.6 2024-08-03 07:05:28 +00:00
DeadlyGenga
db3079271e Removed slayer staff from H.A.M. drop tables 2024-08-03 07:03:10 +00:00
Kamja Imo
9d6ad223cb Added frogspawn and cave eel fishing spots in Lumbridge swamp cave 2024-08-03 07:02:43 +00:00
GregF
3837bd3c26 Corrected option handling on GE interface 2024-08-03 06:59:49 +00:00
GregF
18d5d80fba Fixed issue where green dragons are called Elvargs in slayer tasks 2024-08-03 06:19:31 +00:00
GregF
9d2e173f71 Implemented ground guam as a tar recipe 2024-08-03 05:40:34 +00:00
Player Name
68c1e572b9 Changed the script processor code to no longer incorrectly close interfaces on SOFT queue scripts
Rewrote Jarvald
2024-07-18 09:39:09 +00:00
Ceikry
806680517b Halt tick processing if the server isn't able to reach the internet when watchdog is enabled (configurable via server config options connectivity_check_url and connectivity_timeout) 2024-07-18 08:07:35 +00:00
GregF
53357d20f3 Rewrote Canifis warewolf handling, fixed stats
Fixed bandit aggression
2024-07-13 06:13:31 +00:00
Dark Sage
a44472e559 Added missing sounds
Added equipping sounds to all Kitesheilds & sq shields that are currently available 
Added equipping sounds to combat Melee Boots that are currently available 
Added equipping sounds to combat Melee Gloves that are currently available 
Added equipping sounds to all Coifs that are currently available 
Added equipping sounds to all D'hide bodies that are currently available 
Added equipping sounds to all D'hide chaps that are currently available 
Added equipping sounds to all D'hide vambracers that are currently available 
Added equipping sounds to all Bows that are currently available 
Added equipping sounds to all Cross bows that are currently available 
Added equipping sounds to all Javelins that are currently available 
Added equipping sounds to all Knifes that are currently available 
Added equipping sounds to all Darts that are currently available 
Added equipping sounds to all Throwing axes that are currently available 
Added equipping sounds to all Arrows that are currently available 
Added equipping sounds to all Claws that are currently available 
Added equipping sounds to all Halbergs that are currently available 
Added equipping sounds to all Hastae that are currently available 
Added equiping sounds to all Daggers that are currently available 
Added equipping sounds to all Swords that are currently available 
Added equipping sounds to all Longswords that are currently available 
Added equipping sounds to all Scimitars that are currently available 
Added equipping sounds to all 2H swords that are currently available 
Added equipping sounds to all War-hammers that are currently available 
Corrected the incorrect equipping sounds to all staves to the correct sound
Added equipping sound to the Easter ring
Added equipping sound to the Jack lantern mask
Added the correct red nose light animation to the Reindeer hat
Corrected the Chocatrice cape to be the real one in game with the logo in the back and also added the emote to it
Added sounds to the Scythe and corrected its combat animations
Added the Rubber chicken emote "dance" with its music jingle and the chicken "bawk" sound
Added the Ice amulet and the Wintumber tree to Diangos item return since they were missing
Added the correct jingle to the Air guitar emote
2024-07-13 05:46:28 +00:00
Player Name
6b152bcc8c Wilderness improvements
Added an extra option to the deep wilderness gate warning: "I wish to proceed, and don't ask me again"
Reused the same warning, with appropriate changes, for the edge/ardy levers due to the pvp mechanics
Removed the interface warning when crossing the ditch until doomsayer is implemented
Removed the inauthentic threat revenant and restricted brawler/pvp drops to revenants and chaos elemental only
Now require 100k risk to obtain the boosted deep-wildy drop rates
Hid our inauthentic mechanics behind a new server config option enhanced_deep_wilderness
2024-07-13 05:46:18 +00:00
Bonesy
30f1cbf710 Rewrote unpowered orb charging
Added Seers' Village diary tasks for ranging tickets, talking to Thermac about staves and charging orbs
Corrected explorer's ring alchemy graphics
2024-07-13 04:37:24 +00:00
Bonesy
0165d373bd Rewrote altar praying
Added the Tai Bwo Wannai Trio as quest requirement for praying at the tribal statue
2024-07-13 04:37:21 +00:00
Player Name
6f9a975f7a Added Waterbirth Island and the Dagannoth Kings' lair to admin ::to teleport destinations 2024-07-13 04:37:19 +00:00
Player Name
21a07841f6 Fixed construction door and wall placement
Fixed the bug where some hotspots were still visible even in non-building mode
2024-07-13 04:37:04 +00:00
Player Name
81495ab8a8 ::ge commands now use book interface to support more than 300 listings 2024-07-13 04:24:13 +00:00
GregF
5ed3cafc4a Cuffs, Narf, Jeff and Rusty now have correct examine information
Corrected Narf's stats
Added examine text for all implings and Balnea
2024-07-13 03:33:28 +00:00
GregF
0b95cfdb8f Fixed incorrect reward amount from stronghold of player safety 2024-07-13 03:31:24 +00:00
Player Name
e8527ff506 Your organs will no longer be stolen. Also fixed unexpected disappearance of beds, big plants, bookcases in study, fireplaces in bedroom and costume box in costume room
Fixed incorrect coordinates for the study crystal ball hotspot
2024-07-13 03:21:22 +00:00
Player Name
ef90d24bdc Improved handling of construction bookcase building 2024-07-13 03:16:29 +00:00
Oven Bread
a2b0548c62 Better handling of quest varp/varbit and values to set the quest color to red/yellow/green 2024-07-13 03:15:17 +00:00
Player Name
611660cbc3 Fixed fishing trawler bugs
Optimised fishing trawler reward logic
2024-07-13 02:58:24 +00:00
Kamja Imo
370d9ef423 Removed quest completion check on fairy ring exit from Zanaris 2024-06-27 14:11:43 +00:00
Doggo
8d2b454b1c Fixed salamander attack style 2024-06-23 13:11:23 +00:00
Doggo
28b168e742 Fixed 1 tick movement delay on some actions 2024-06-08 12:23:06 +00:00
Player Name
98c0698097 Fixed random events occurring at kalphite queen and tormented demons
Fixed bots trying to speak unicode
2024-06-08 12:17:37 +00:00
Player Name
80a5c3a85e Enabled Lunar Isle and Miscellania coal mine star locations
Fixed requirements checking for some star locations
2024-06-08 12:15:46 +00:00
thelemir
ca3bc9cd70 Rewrote Rimmington
Enabled pickpocketing of Anja and Hengel
2024-06-08 12:13:22 +00:00
Player Name
2c3b5cb5a4 Fixed MTA telekinetic theatre statue path check 2024-06-08 11:55:23 +00:00
Oven Bread
5f62047aff Fixed the Druidic Ritual quest log rendering
Implemented two bank noticeboards
2024-06-08 11:25:03 +00:00
Oven Bread
d0f24afc2e Implemented White Wolf Mountain & Ice Queen 2024-06-08 11:24:25 +00:00
Bonesy
1f6b0e900f Fixed bug causing loss of xp when interrupted burying bones 2024-06-08 11:14:03 +00:00
Doggo
ddb9b6f0b0 Fixed NPCs pathing around safespots when standing adjacent to the safespot 2024-06-08 10:17:07 +00:00
Ceikry
45c21767d5 Reduced price of fighter torso to 4.5M (until barbarian assault is implemented) 2024-06-08 10:17:01 +00:00
João Victor
f01a5cfe40 Fixed incorrect description on a fairy ring 2024-06-08 10:15:53 +00:00
1194 changed files with 90001 additions and 21693 deletions

View file

@ -149,4 +149,4 @@ These credits can be spent in the 2009Scape Reward Shop. It's important to be cl
Testers are not the only people who can gain credits - other ways of earning credits can be found on [the 2009Scape website](https://2009scape.org/site/game_guide/credits.html).
Please be patient! The Credit system is not fully complete yet, so it will take a long time for credits to be awarded.
Please be patient! The Credit system is not fully complete yet, so it will take a long time for credits to be awarded.

1
Server/.gitignore vendored
View file

@ -2,6 +2,7 @@ bin/**
out/**
data/logs/**
data/profile/**
data/playerstats/**
.idea/**
/bin
.DS_Store**

View file

@ -25,4 +25,7 @@
<ObjectAction mode="add" id="21301" x="2662" y="3162" z="0" direction="e"/>
<ObjectAction mode="add" id="4176" x="2629" y="3661" z="2" type="4" direction="e"/>
<ObjectAction mode="add" id="2489" x="2839" y="3282" z="0" direction="w"/>
<ObjectAction mode="add" id="36695" x="2894" y="3510" z="0" direction="w"/>
</parsing>

View file

@ -257,7 +257,7 @@
"What do you think @name?",
"How does that sound @name?",
"That sounds great @name.",
"Im learning English.",
"I'm learning English.",
"I don't understand.",
"Could you repeat that please @name?",
"Could you please talk slower @name?",
@ -301,7 +301,7 @@
"Always check announcments",
"We thrivin",
"Ship @name",
"Dont forget to vote 2009Scape!",
"Dont forget to vote 2009scape!",
"Kermit is too legit 2 quit",
"Out here on the range we are having fun",
"I am hank steel",
@ -338,7 +338,7 @@
"I couldn't agree more @name",
"It cost me a fortune @name",
"I am dog tired",
"Dont take it personally",
"Don't take it personally",
"We will be having a good time",
"Same as always @name",
"No problem",
@ -367,33 +367,16 @@
"May Guthix bring you balance.",
"May Guthix bring you balance.",
"May Guthix bring you balance.",
"Thy death was not in vain, for it brought some balance to the world. May Guthix bring you rest.",
"May you walk the path, and never fall, for Guthix walks beside thee on thy journey. May Guthix bring you peace.",
"All things must end, as all begin; Only Guthix knows the role thou must play. May Guthix bring you balance.",
"In life, in death, in joy, in sorrow: May thine experience show thee balance. May Guthix bring you balance.",
"Thou must do as thou must, no matter what. Thine actions bring balance to this world. May Guthix bring you balance.",
"The river flows, the sun ignites, May you stand with Guthix in thy fights. May Guthix bring you balance.",
"A journey of a single step, May take thee over a thousand miles. May Guthix bring you balance.",
"Zamorak give me strength!",
"Zamorak give me strength!",
"Zamorak give me strength!",
"May your bloodthirst never be sated, and may all your battles be glorious. Zamorak bring you strength.",
"There is no opinion that cannot be proven true...by crushing those who choose to disagree with it. Zamorak give me strength!",
"Battles are not lost and won; They simply remove the weak from the equation. Zamorak give me strength!",
"Those who fight, then run away, shame Zamorak with their cowardice. Zamorak give me strength!",
"Battle is by those who choose to disagree with it. Zamorak give me strength!",
"Strike fast, strike hard, strike true: The strength of Zamorak will be with you. Zamorak give me strength!",
"The weak deserve to die, so the strong may flourish. This is the creed of Zamorak.",
"This is Saradomin's wisdom.",
"This is Saradomin's wisdom.",
"This is Saradomin's wisdom.",
"Go in peace in the name of Saradomin; may his glory shine upon you like the sun.",
"Thy cause was false, thy skills did lack; See you in Lumbridge when you get back.",
"Protect your self, protect your friends. Mine is the glory that never ends. This is Saradomin's wisdom.",
"The darkness in life may be avoided, by the light of wisdom shining. This is Saradomin's wisdom.",
"Show love to your friends, and mercy to your enemies, and know that the wisdom of Saradomin will follow. This is Saradomin's wisdom.",
"A fight begun, when the cause is just, will prevail over all others. This is Saradomin's wisdom.",
"The currency of goodness is honour; It retains its value through scarcity. This is Saradomin's wisdom.",
"For Camelot!",
"Firmly grasp it!",
"My legs!",
@ -496,21 +479,525 @@
"Shlisshalpshlaap",
"Somebody call for an exterminator?",
"Decisive action. Should work.",
"R E A D Y T O R A I S E S O M E H E L L",
"Darkness overpowering.",
"Got any questions about propane? Or propane accessories?",
"When someone asks me if I am a god, I say Y E S!!!",
"You run out of Marines?",
"Goliath Online",
"I'm escaping to the one place that hasn't been corrupted by Capitalism. Lunar Isle!",
"Selling wildy protection, 100k gp",
"Cant we get you on Mastermind, @name? Next contestant @name from Lumbridge. Special subject the bleedin obvious.",
"Oh, youre German! Im sorry, I thought there was something wrong with you.",
"Listen, dont mention the war! I mentioned it once, but I think I got away with it all right.",
"Can't we get you on Mastermind, @name?",
"Listen, don't mention the war! I mentioned it once, but I think I got away with it all right.",
"Cunnilingus and psychiatry brought us to this.",
"Oh, poor baby. What do you want, a Brimstail's Sampler?",
"There's an old TzHarrian saying, you fuck up once, you lose two teeth.",
"The lineup consisted simply of six hydrocoptic marzelvanes so fitted to the ambifacient lunar waneshaft that sidefumbling was prevented."
"Ah, the Wilderness... where dreams of riches meet a swift demise.",
"Who needs a quest guide? I've got the whole wiki memorized.",
"Buying GF 10k coins. Must have at least 70 Agility.",
"I swear, the goblins in Goblin Village have it out for me.",
"Why do wizards always hang out in towers? Is it a zoning thing?",
"I'm training my Construction skill. My house is gonna be lit!",
"The GE is like the stock market, but with dragon bones.",
"Why do we need a cabbage patch in Draynor? Seriously.",
"I've been mining rune essence for hours. My pickaxe hates me.",
"@name hit 99 Cooking. Time to open a gourmet restaurant in Varrock.",
"I'm convinced the @name is actually a time-traveling wizard.",
"Anyone up for a Castle Wars match? I need that decorative armor.",
"Accidentally clicked Attack on a guard. Now I'm a wanted criminal.",
"Why do we even have a Duel Arena? It's just a fancy boxing ring.",
"I've got a stack of burnt lobsters. Anyone want to buy them?",
"Greetings, @name! Looking for a quest?",
"Just got a rare drop! The RNG gods are smiling upon me!",
"Anyone need help with a boss fight? I've got my dragon dagger ready.",
"Buying feathers! Will pay top price!",
"The Lumbridge cows are my favorite training spot.",
"Who needs a teleport? I've got my magic runes stocked.",
"@name hit 99 Woodcutting. Time to chop some yews!",
"Anyone seen the Wise Old Man lately? I owe him a visit.",
"Selling lobsters! Freshly caught from Catherby.",
"Swear, the Wilderness is scarier than my nightmares.",
"Looking for a clan to join. Any takers?",
"I'm an ironman, so no trading for me!",
"Anyone up for a Castle Wars match?",
"The Grand Exchange prices are crashing. Panic sell!",
"Who else remembers the Falador Massacre?",
"I'm saving up for a party hat. Wish me luck!",
"Barrows runs are my addiction. Those crypts are spooky.",
"I'm convinced the Wise Old Man is secretly Zamorak.",
"I'm a completionist, so I'm grinding out all the achievements.",
"The music in this game is surprisingly epic.",
"Tried to trade with a tree... It didn't go well.",
"My bank is like a black hole. Items disappear forever.",
"I'm convinced the Wise Old Man is just a confused tourist.",
"My character's fashion sense? Let's just say it's 'unique.'",
"Challenged a cow to a dance-off. It won.",
"Why does the Lumbridge Guide always look so lost?",
"I'm training Agility, but my real-life agility is zero.",
"Tried to fish in the desert. Sandfish are elusive.",
"The Grand Exchange is like a chaotic stock market.",
"I'm a master at clicking 'Continue' during quests.",
"I'm convinced the chickens are plotting world domination.",
"I'm a woodcutter, but I've never seen a talking tree.",
"I'm a vegetarian, except when it comes to killing dragons.",
"Why do wizards wear pointy hats? Is it a fashion statement?",
"Challenged a guard to a staring contest. He won.",
"I'm collecting cabbage. It's a noble pursuit.",
"Accidentally set my cat on fire. It's now a firecat.",
"I'm convinced the Wise Old Man is secretly a time traveler.",
"I'm a quest completionist, but I still can't find my keys.",
"I'm convinced the ducks are spying on us.",
"I'm a pro at avoiding the Lumbridge swamp. Too many frogs.",
"Hey, can someone lend me 10k? I promise I'll pay it back... eventually.",
"Why do I always find the one square where the random event spawns?",
"I'm training Prayer by burying bones. It's like a spiritual workout.",
"The Wise Old Man's fashion sense is questionable.",
"I'm convinced the ducks are secretly plotting world domination.",
"Why do wizards wear pointy hats? Is it a dress code?",
"I'm collecting cabbage. It's a noble pursuit, really.",
"I'm a vegetarian, except when it comes to killing dragons.",
"Why does the Lumbridge Guide always look so lost?",
"Training Agility, but my real-life agility is zero.",
"I'm convinced the Wise Old Man is secretly a time traveler.",
"Tried to mine air. It's an untapped resource.",
"I'm a quest completionist, but I still can't find my keys.",
"Challenged a guard to a staring contest. He won.",
"I'm saving up for a party hat. Priorities, you know?",
"Why do goblins drop coins? Do they moonlight as accountants?",
"I'm convinced the chickens are plotting something.",
"Clicked 'Attack' on a chicken. Now I'm a poultry murderer.",
"My character's fashion sense? Let's just say it's 'unique.'",
"Do you know where I can find the entrance to the Taverley Dungeon?",
"Hey, anyone here familiar with the Barrows? I need some tips.",
"Is there a bank nearby? My inventory is overflowing with loot",
"I'm clueless about clue scrolls",
"Where's the best spot to catch sharks? I'm aiming for 99 Fishing.",
"Is there a shortcut to the Karamja Volcano?",
"Can someone explain the mechanics of the Jad fight in the Fight Caves?",
"I'm stuck on the Elemental Workshop quest. Any hints?",
"Where can I find a loom to spin flax into bowstrings?",
"What's the best gear setup for killing dragons? I want that visage drop!",
"I'm trying to unlock the fairy rings",
"How do I recharge my amulet of glory?",
"Why do goblins drop coins?",
"The Grand Exchange is like the stock market",
"My real-life agility is more like a lumbering tortoise.",
"The Wise Old Man's fashion sense is like Woahs",
"I challenged a cow to a dance off, surprisingly smooth mooves.",
"Why does the Lumbridge Guide always look lost?",
"I'm convinced the ducks in Lumbridge are plotting world domination!",
"I accidentally ate my prayer potion I'm blessed with heartburn",
"I'm a vegetarian, except when it comes to slaying dragons.",
"Why do wizards wear pointy hats? Is it a magical dress code?",
"I'm a quest completionist, but I still can't find my keys in real life.",
"I challenged a guard to a staring contest. He won.",
"I'm saving up for a party hat. Priorities, ya know?",
"What's the deal with the Wilderness?",
"I'm convinced the chickens are secretly plotting world domination!",
"Accidentally clicked 'Attack' on a guard. Now I'm on a watchlist.",
"My character's fashion sense? Let's just say it's 'unique'",
"Lobsters heal my soul",
"Trimmed armor or bust!",
"Wilderness: Where friendships go to die",
"Buying gf 10k",
"Dancing for coins at Lumbridge",
"Wearing full rune like a boss",
"Teleporting to Camelot for quests",
"Fishing for hours at Catherby",
"PKing with a rune 2h",
"World 1 Falador Park parties",
"Staking my bank at the Duel Arena",
"Dying to Elvarg's fiery breath sums up my life",
"Got the Quest cape finally on my main!",
"Wearing a party hat with pride",
"Castle Wars: Red vs Blue",
"Trading in Varrock Square",
"Barrows runs for that sweet loot",
"Spending hours in Pest Control is life",
"Killing cows for leather armor is a good start",
"Farming herbs in Ardougne can be good money",
"Clan chat drama LOL",
"Mining rune essence endlessly",
"Agility courses: A love-hate relationship",
"Dueling for honor.... and GP",
"Buying runes from Aubury is decent money",
"Chasing the Easter Bunny like a scam",
"Wearing a skillcape with pride",
"Fletching yew longbows for profit",
"Going to implings in Puro-Puro",
"Castle Wars barricade wars",
"Dropping party hats at drop parties like pennies",
"Killing lesser demons in Karamja is part of my holy conquest",
"@name lures noobs into the Wilderness",
"Farming ranarrs for cash",
"Getting lost in the Underground Pass",
"Selling coal at the Grand Exchange",
"Fighting the Kalphite Queen",
"Picking flax in Seers' Village",
"Begging for free stuff in Lumbridge",
"Smithing rune platebodies for profit",
"Logging out in Lumbridge Castle",
"Hey @name, how do I get to the Grand Exchange?",
"Veteran here! @name, what's your favorite quest?",
"@name, how do I make money fast?",
"Hey @name, what's the best combat style for bossing?",
"@name, always carry an emergency teleport!",
"New player here! @name, what's the Wilderness like?",
"Hey @name, how do I join a clan?",
"@name, prioritize Prayer levels!",
"@name, what's the best way to train Agility?",
"Hey @name, what's your favorite minigame?",
"@name, never trust a PKer in Lumbridge!",
"@name, how do I get a fire cape?",
"Hey @name, what's the fastest way to level up Magic?",
"@name, try the Barrows tunnels!",
"@name, what's the deal with Pest Control?",
"Hey @name, how do I unlock the Fairy Rings?",
"@name, remember the old random events?",
"@name, should I train Strength or Attack first?",
"Hey @name, what's your favorite skillcape?",
"@name, can't do the Fight Caves!",
"@name, how do I get a pet?",
"Hey @name, what's the best food for boss fights?",
"@name, Castle Wars or Clan Wars?",
"@name, what's a clue scroll?",
"Hey @name, how do I defeat Jad?",
"@name, use Protect from Melee at KBD!",
"@name, should I train Ranged or Magic?",
"Hey @name, what's the best way to level up Crafting?",
"@name, don't forget your anti-dragon shield!",
"@name, what's the Stronghold of Security?",
"Hey @name, how do I access the Legends' Guild?",
"@name, remember the old PvP worlds?",
"@name, how do I get a dragon defender?",
"Hey @name, what's the best way to level up Herblore?",
"@name, always carry a charged amulet of glory!",
"@name, should I train Fishing or Cooking?",
"Hey @name, how do I defeat the Chaos Elemental?",
"@name, try the TzHaar Fight Pit!",
"@name, what's the best way to level up Smithing?",
"Hey @name, what's your favorite Slayer master?",
"@name, use the Ardougne cloak teleports!",
"@name, what's the Legends' Quest about?",
"Hey @name, how do I unlock the Ancient Magicks?",
"@name, remember the old Pest Control boats?",
"@name, what's the best way to level up Construction?",
"Hey @name, what's the fastest way to level up Prayer?",
"@name, the old Duel Arena stakes!",
"@name, what's the Warriors' Guild?",
"Hey @name, how do I defeat the Kalphite Queen?",
"@name, use the fairy ring code BIP!",
"@name, should I train Attack or Defense first?",
"Hey @name, what's the best way to level up Thieving?",
"@name, don't forget your charged glory amulet!",
"@name, what's the Legends' Cape?",
"Hey @name, how do I access the Heroes' Guild?",
"@name, remember the old Pest Control void gear?",
"@name, how do I get a firemaking skillcape?",
"Buying gf 10k",
"Trimming armor for free!",
"Wanna join my clan? We're called The Mighty Cabbages'!",
"I'll meet you at the Falador Party Room!",
"Dancing for coins in Varrock Square!",
"Remember when the Wilderness was dangerous?",
"I got a rune scimitar drop from Lesser Demons!",
"Lumbridge Swamp is haunted!",
"Anyone up for Castle Wars?",
"I miss the old random events!",
"World 2 is the trading hub!",
"I'm going to train my Agility at the Gnome Stronghold!",
"I just got 99 Cooking!",
"Fishing lobsters at Catherby is life!",
"The Legends' Guild is so exclusive!",
"Who needs a quest guide? I'll figure it out!",
"I'm mining rune essence for hours!",
"Barrows armor looks sick!",
"I'm stuck in the Underground Pass!",
"The Stronghold of Security taught me about account security!",
"I'm going to farm herbs in Ardougne!",
"I'm getting my fire cape!",
"I love the music in RuneScape!",
"I'm alching my maple longbows!",
"I'm doing the Recipe for Disaster subquests!",
"I'm going to train my combat stats at the Rock Crabs!",
"I'm going for the Quest Cape!",
"I'm going to mine pure essence!",
"I'm hunting chinchompas in the Feldip Hills!",
"I'm going to train my Woodcutting at Seers' Village!",
"I'm going to fish sharks at the Fishing Guild!",
"I'm going to train my Thieving at Ardougne Knights!",
"I'm going to hunt implings in Puro-Puro!",
"I'm going to train my Hunter at the Falconry!",
"I'm smithing rune platebodies!",
"I'm going to train my Farming at the Tree Gnome Stronghold!",
"I'm going to hunt red chinchompas in the Wilderness!",
"I'm doing the Underground Pass quest!",
"I'm going to fish monkfish in Piscatoris!",
"Meet me in Varrock, @name, for an epic trade",
"@name, join my clan; we'll conquer the Wilderness together!",
"Beware the dragons, @name, they're fiercer than you think",
"Crafting runes with @name, the best mage in Gielinor",
"Fishing lobsters with @name, the sea's no match for us",
"@name, your swordsmanship at Duel Arena is unmatched!",
"Questing through dark caves, @name always leads the way",
"Share your wisdom, @name, how'd you master those spells?",
"Legends speak of @name bravery at the God Wars",
"Need more arrows, @name? I've got plenty to spare",
"Cooking's a breeze when @name around, no burnt lobsters!",
"Mining together, @name and I strike gold every time",
"@name, let's barter; your herbs for my potions?",
"Training agility with @name, leaping like graceful gazelles",
"Heard @name got the best magic beans in town",
"Fletching bows with @name, aiming for perfection",
"Smithing with @name, our anvils never cool down",
"Adventuring with @name, every quest is a thrill",
"Battling demons, @name courage inspires us all",
"Slaying dragons, @name the hero we need",
"Gathering at Falador, @name party room is legendary",
"Hunting chinchompas, @name traps are always full",
"Farming's fun with @name, our crops never fail",
"Brewing potions, @name mixtures are magical",
"Casting spells with @name, we're invincible",
"Building fires, @name flames warm the coldest nights",
"Sailing to Pest Control, @name our fearless captain",
"Trading runes, @name deals are the fairest",
"Exploring dungeons, @name the light in the darkness",
"Charging orbs with @name, our energy knows no bounds",
"Dancing in Draynor, @name moves are enchanting",
"Playing Gnomeball, @name the star player",
"Harvesting willows, @name axe swings true",
"Enchanting jewelry, @name touch turns copper to gold",
"Summoning familiars, @name spirit wolf leads the pack",
"Thieving from stalls, @name hands are lightning-fast",
"Crafting runes, @name essence never runs dry",
"Fishing at Catherby, @name catch feeds us all",
"Cooking feasts, @name dishes delight the gods",
"Mining runite, @name pickaxe strikes rich veins",
"Bartering at Grand Exchange, @name a trading master",
"Training prayer, @name piety moves mountains",
"Fighting revenants, @name valor shines bright",
"Building homes, @name construction is flawless",
"Hunting imps, @name net is always full",
"Brewing ale, @name tavern is the town's favorite",
"Casting high alchemy, @name turns junk into treasure",
"Sailing to Karamja, @name adventures are legendary",
"Forging alliances, @name charisma unites clans",
"Defeating bosses, @name name echoes in legends",
"Are you mewing @name???",
"Check out that gyatt @name",
"Bruhhhhh @name got that rizz",
"@name rizzing up the bots",
"Ironman? More like copperboy LOL",
"What that gyatt do @name",
"He's got the zoomies!",
"@name likes pickles and dipped in mayo",
"Lmaaooooooo",
"Bruh she said she loved me...",
"I caught @name rizzing a mewing teacher",
"What that mouth do bb",
"Ayo tf he say",
"Ayo",
"Ayo @name a freak lowkey",
"Ayo tf",
"@name catch me outside howbout that",
"Wasssssuuuuppppppp",
"Knock knock @name",
"Oh boy howdy do i have a surprise for you",
"Noooooo",
"What do you mean I haven't done anything wtf",
"Redrocket redrocket!!!",
"I made 20,000 crochet sock puppets for ceikry",
"Come on @name",
"Are we rading tonight @name?",
"Why would he say that",
"Penguins are technically reptiles",
"Brb smell something burning",
"Need pest control partner, you handle the portals, i will afk",
"Bruhhhh the skibiddi rizz in my gyatt makes my mewing sesh rough",
"@name jajajajaja",
"Wait, I can't raid tonight @name",
"Brb, pizza's here. Hope they ask why I'm a grown man dressed like an elf",
"Sorry, gotta go AFK. My dog just ate my gaming headset.",
"Brb, my grandma just fell down the stairs",
"Hold on, the baby's crying, aka @name",
"Oops, spilled my drink",
"Guys, I need to log off, my plants are staging a revolt for not watering them",
"AFK a sec, my neighbor's llama is in my backyard again",
"My pizza rolls are ready",
"Lost track of time, i'm late for my own wedding",
"Sorry, can't hear you over the sound of my laundry",
"Turn HDR off in windows @name",
"Www. no one cares .com",
"Hey @name www dot stfu dot com",
"Like pitching a tent in a pair of britches",
"Mad as a bag of ferrets",
"I dont think beavers built the hoover dam",
"How do beavers get the concrete for dams?",
"@name show me the way",
"@name onwards brutha",
"@name eats fried rat tails",
"@name is a rat",
"Why are there so many people farming right now",
"@name fuck the police",
"@name says the like pickles on their hotdogs",
"Fuck the guards, free woah in varrock prison",
"Ayo fuck u mean",
"Woop woop thats da sound of the beast",
"How are you the way that you are",
"She sells seashells by the sea shore",
"@name certified rat",
"@name buys skimmed milk",
"Got milk?",
"Are you JSON because i want you to get array from me",
"@name likes string manipulation",
"@name sells legos",
"JUST DO IT",
"Why are there so many motherfucking options",
"Just hit the fucking auto hide thing",
"Just play",
"Hell no this shit is fuzzy as balls",
"@name is fuzzy as balls",
"Turn your brightness up @name",
"On my pc it runs perfectly",
"I know why mine seems a little jumpy",
"Stream the window not the monitor",
"Discords a bitch",
"Why are there so many handsome people at the ge like @name",
"I'll make you squeel of fortune",
"You will get it in due time @name",
"I was one turn away",
"It do be pipe time @name",
"Sometimes i like to cover myself in vaseline and pretend i'm a slug",
"Woah is a greased pig",
"Sometimes i dig holes in my backyard and pretend i'm a carrot",
"It's pipe time @name",
"If i am not pipe timing i am programming",
"Yes i have thigh high socks, no you cannot have them",
"I scream when i wear my programming socks",
"Anyone wanna buy @name's bathwater?",
"Anyone wanna buy my used socks?",
"@name be getting bags",
"What the fuck is this",
"Hi i noticed you haven't taken a break in hours @name",
"I really am seething rn",
"I am so fucking tilted",
"Go back to your fucking frogs",
"Life is simple as frog farmer",
"A q p",
"Oooooh that's a first",
"Been a minute @name",
"Hey @name",
"Oh shit it's @name",
"Hey @name wyd today?",
"Ayo @name",
"Wyd @name",
"Wbu @name?",
"Good fuck em, that's what they get",
"Deathknight lookin rat ass mf @name",
"@name sucks slugs",
"Brb gotta take a shit",
"@name will brb went to take a shit",
"Tell me when he is coming",
"This guy is being incredibly based",
"@name is based af",
"Taking l's all day today @name",
"That sucks massive dongus",
"Ah i was about to type that",
"Where is stormwind?",
"How do i buy gold",
"@name sells gold",
"@name is a gold digger",
"Ayo @name wanna buy some frog legs?",
"Brb fucking burnt my pizza rolls",
"I swear @name if you need roll again",
"@name you are a hunter you don't need plate armor",
"Dragonriding is satisfying",
"There's an old TzHarrian saying, you fuck up once, you lose two teeth",
"FUCK",
"@name i am ready when you are",
"Anyone selling logs?",
"That is hilarious @name",
"Alright @name",
"I need to go get my quest cape",
"500 barrows runs dry",
"Back to back to back barrows items, easy game",
"@name did you know them?",
"@name sucks eggs",
"@name eats snails",
"Who is that?",
"I've gotta go in 20 minutes",
"Bonk",
"Gtg in 30 minutes",
"They left 50 minutes ago",
"I think it's just gonna be us",
"Who is that?",
"Stop following me",
"@name stop following me",
"I think @name is watching me",
"@name wouldn't let me get a hit in at clan wars",
"Clan wars and chill?",
"Quick mewing sesh",
"Don't you stickbug me",
"Get stick bugged",
"Wtf why are you a dragon",
"Fun fact, woah eats eggs whole like a snake",
"Ceikys there's a rare over there",
"Where is goldshire?",
"How long have you played @name?",
"Wanna quest?",
"TROGDORRRRRR",
"Fuck this shit i'm out",
"Wait till you get your first 99",
"How is that even possible",
"That's the max",
"@name is trying to max",
"Complesionist cape?",
"How can you increase your run speed?",
"@name where is barrows?",
"@name where are rune rocks?",
"@name where is yanille?",
"@name how long have you played for?",
"Yes these quotes were hand typed",
"Kermit was here",
"World of Warcraft died after Wrath",
"Ceikry needs to add talent trees",
"How do you get to the wilderness?",
"Anyone tryna lure @name?",
"@name tried to lure me",
"@name bots all the time",
"Anyone want chicken tendies",
"Brb foods ready",
"Brb",
"Gotta go get food",
"Anyone else from northern alaska?",
"Fishing 4 gp",
"Looking for gf",
"Anyone wanna be my discord kitten?",
"Selling discord kittens",
"@name is a discord kitten",
"Add me on discord",
"Are you in the 09discord",
"Where are you going @name?",
"Idk what i am going to cook today",
"What should i eat tonight",
"He is in the cave",
"Don't forget to save game before logging out",
"American horror story died after season 1",
"Prequels are better than the sequels",
"Disney star wars is the best star wars",
"Halo 5 sucked",
"Selling xbox live gold and 1600 microsoft points for 50k gp",
"Buying gf 25gp and a bucket",
"@name i heard evilwaffles bots",
"@name hopefully ceikry doesn't find out about evilwaffles",
"Honk honk",
"Where is the road to mordor?",
"Golem lookin ass bitch",
"@name rat looking mf",
"@name uses sand paper to wipe",
"@name said Woah is cute",
"You need 25 of 40 to do that",
"Over 1000 unique lines of dialogue",
"What should i get to eat",
"Ge be poppin today",
"Where all the woahs at",
"Ayo woahscam got that gyatt",
"That is one i haven't seen before"
],
"halloween": [
"Trick or treat!!!",
@ -521,9 +1008,9 @@
"Costume party at my P O H!! Follow me!",
"Trick, then!",
"Brrrainssssssss...",
"'Cause this is thriller, thriller night, and no one's gonna save you from the beast about to strike!",
"This is Hallowe'en, this is Hallowe'en, pumpkins scream in the dead of night!",
"This is Hallowe'en, everybody make a scene, Trick or treat till the neighbors gonna die of fright!",
"'Cause this is thriller, thriller night!",
"This is Hallowe'en, this is Hallowe'en!",
"This is Hallowe'en, everybody make a scene!",
"In this town we call home, everyone hail to the pumpkin song!",
"Watch out for Skeleton Jack!",
"The headless-what-man? Horse? Never heard of those.",

File diff suppressed because it is too large Load diff

BIN
Server/data/cache/main_file_cache.dat2 (Stored with Git LFS) vendored

Binary file not shown.

BIN
Server/data/cache/main_file_cache.idx255 (Stored with Git LFS) vendored

Binary file not shown.

BIN
Server/data/cache/main_file_cache.idx5 (Stored with Git LFS) vendored

Binary file not shown.

View file

@ -301,7 +301,7 @@
},
{
"id": "2025",
"replaceId": "1534",
"replaceId": "2026",
"fence": "false",
"metal": "false"
},
@ -312,7 +312,7 @@
"metal": "false"
},
{
"id": "2036",
"id": "1530",
"replaceId": "1531",
"fence": "false",
"metal": "false"
@ -925,7 +925,7 @@
},
{
"id": "3747",
"replaceId": "1534",
"replaceId": "3748",
"fence": "false",
"metal": "false"
},
@ -943,7 +943,7 @@
},
{
"id": "4148",
"replaceId": "4248",
"replaceId": "4246",
"fence": "false",
"metal": "false"
},
@ -1759,7 +1759,7 @@
},
{
"id": "14245",
"replaceId": "14248",
"replaceId": "14246",
"fence": "true",
"metal": "false"
},
@ -1984,16 +1984,14 @@
"replaceId": "28518",
"fence": "false",
"metal": "true",
"autowalk": "true",
"questRequirement": "Icthlarin's Little Helper"
"autowalk": "true"
},
{
"id": "28514",
"replaceId": "28518",
"fence": "false",
"metal": "true",
"autowalk": "true",
"questRequirement": "Icthlarin's Little Helper"
"autowalk": "true"
},
{
"id": "21065",

File diff suppressed because it is too large Load diff

View file

@ -233,7 +233,7 @@
},
{
"item_id": "946",
"loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,3218,9887,0,33}-"
"loc_data": "{1,2903,3148,0,80}-{1,3205,3212,0,80}-{1,3224,3202,0,80}-{1,3218,3416,1,90}-{1,2820,3450,0,90}-{1,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,3218,9887,0,33}-{1,2700,3407,0,100}-"
},
{
"item_id": "952",
@ -241,7 +241,7 @@
},
{
"item_id": "954",
"loc_data": "{1,2094,3152,0,150}-"
"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",
@ -339,6 +339,14 @@
"item_id": "1422",
"loc_data": "{1,3320,3137,0,150}-"
},
{
"item_id": "1467",
"loc_data": "{1,2784,3289,0,0}-"
},
{
"item_id": "1469",
"loc_data": "{1,2766,3277,0,100}-{1,2766,3289,0,100}-"
},
{
"item_id": "1510",
"loc_data": "{1,2576,3334,0,100}-"
@ -619,6 +627,10 @@
"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}-"
@ -627,6 +639,10 @@
"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}-"

View file

@ -95,6 +95,30 @@
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "68",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "69",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "70",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "71",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "74",
"interfaceType": "4",
@ -365,6 +389,30 @@
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "245",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "246",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "247",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "248",
"interfaceType": "4",
"walkable": "false",
"tabIndex": "-1"
},
{
"id": "256",
"interfaceType": "8",

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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]}"
}
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -16324,7 +16324,7 @@
"ids": "11629"
},
{
"examine": "South to Falador :: West to Taverley :: East to Varrock.",
"examine": "This tells you which way is which.",
"ids": "11630,11631,11632,11633"
},
{
@ -16741,7 +16741,7 @@
},
{
"examine": "Better not eat them!",
"ids": "12003"
"ids": "12003,12128"
},
{
"examine": "It's going to be a tight squeeze!",
@ -16881,7 +16881,7 @@
},
{
"examine": "A wall jutting out into the path.",
"ids": "12127,12128,12129,12130"
"ids": "12127,12129,12130"
},
{
"examine": "Spooky.",

View file

@ -213,7 +213,7 @@
"general_store": "true",
"id": "25",
"title": "Lumbridge General Store",
"stock": "{1931,30,100}-{1935,30,100}-{1735,10,100}-{1925,10,100}-{1923,10,100}-{1887,10,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{550,10,100}-{9003,10,100}"
"stock": "{1931,30,100}-{1935,30,100}-{1735,10,100}-{1925,10,100}-{1923,10,100}-{1887,10,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{550,10,100}-{9003,10,100}-{1939,1000,1}"
},
{
"npcs": "4716",
@ -564,7 +564,7 @@
"general_store": "false",
"id": "65",
"title": "Dodgy Mikes Second-hand Clothing",
"stock": "{7114,10,100}"
"stock": "{7114,10,100}-{7122,10,100}-{7128,10,100}-{7134,10,100}-{7110,10,100}-{7126,10,100}-{7132,10,100}-{7138,10,100}-{7116,10,100}-{7124,10,100}-{7130,10,100}-{7112,10,100}-{7136,10,100}"
},
{
"npcs": "2161",
@ -1599,7 +1599,7 @@
"general_store": "false",
"id": "181",
"title": "MoonClan Fine Clothes",
"stock": "{9068,10,100}-{9069,10,100}-{9070,10,100}-{9071,10,100}-{9072,10,100}-{9073,10,100}-{9074,10,100}-{1733,50,100}-{1734,500,100}"
"stock": "{9068,10,100}-{9069,10,100}-{9070,10,100}-{9071,10,100}-{9072,10,100}-{9073,10,100}-{9074,10,100}-{1733,50,100}-{1734,500,100}-{9084,10,100}-{9096,10,100}-{9097,10,100}-{9098,10,100}-{9099,10,100}-{9100,10,100}-{9101,10,100}-{9102,10,100}-{9104,10,100}"
},
{
"npcs": "",
@ -2177,5 +2177,23 @@
"id": "255",
"title": "Castle Wars Ticket Exchange",
"stock": "{4068,1,100}-{4069,1,100}-{4070,1,100}-{4071,1,100}-{4072,1,100}-{4503,1,100}-{4504,1,100}-{4505,1,100}-{4506,1,100}-{4507,1,100}-{4508,1,100}-{4509,1,100}-{4510,1,100}-{4511,1,100}-{4512,1,100}-{4513,1,100}-{4514,1,100}-{4515,1,100}-{4516,1,100}"
},
{
"npcs": "5111",
"high_alch": "0",
"currency": "995",
"general_store": "false",
"id": "256",
"title": "Leon's Prototype Crossbow",
"stock": "{10156,2,100}"
},
{
"npcs": "2039",
"high_alch": "0",
"currency": "995",
"general_store": "false",
"id": "256",
"title": "Uglug's Stuffsies",
"stock": "{4844,100,10}-{10927,0,100}-{2862,100,100}-{1777,10,200}-{2876,0,500}-{2878,10,100}-{4850,0,100}-{946,5,100}-{4773,0,1000}-{4778,0,1000}-{4783,0,1000}-{4788,0,1500}-{4793,0,2000}-{4798,0,3000}-{4803,0,4000}-{4827,0,2000}"
}
]

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ 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
@ -304,7 +305,7 @@ enum class EnchantedJewellery(
return
}
sendNPCDialogue(player, slayerManager.master!!.npc, "You're currently " +
"assigned to kill ${getSlayerTaskName(player).lowercase(Locale.getDefault())}'s; " +
"assigned to kill ${SlayerUtils.pluralise(getSlayerTaskName(player))} " +
"only ${getSlayerTaskKillsRemaining(player)} more to go.", core.game.dialogue.FacialExpression.FRIENDLY)
// Slayer tracker UI
setVarp(player, 2502, slayerManager.flags.taskFlags shr 4)

View file

@ -100,7 +100,7 @@ public enum GodBook {
* @param page the page.
*/
public void insertPage(Player player, Item book, Item page) {
if (!hasRequirement(player, "Horror from the Deep"))
if (!hasRequirement(player, Quests.HORROR_FROM_THE_DEEP))
return;
if (hasPage(player, book, page)) {
player.sendMessage("The book already has that page.");

View file

@ -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
}
}

View file

@ -30,36 +30,36 @@ public enum Consumables {
HERRING(new Food(new int[] {347}, new HealingEffect(5))),
MACKEREL(new Food(new int[] {355}, new HealingEffect(6))),
ROAST_BIRD_MEAT(new Food(new int[] {9980}, new HealingEffect(6))),
THIN_SNAIL(new Food(new int[] {3369}, new HealingEffect(5))),
THIN_SNAIL(new Food(new int[] {3369}, new RandomHealthEffect(5, 7))),
TROUT(new Food(new int[] {333}, new HealingEffect(7))),
SPIDER_ON_STICK(new Food(new int[] {6297, 6305}, new HealingEffect(7))),
SPIDER_ON_STICK(new Food(new int[] {6297, 6305}, new RandomHealthEffect(7, 11))),
SPIDER_ON_SHAFT(new Food(new int[] {6299}, new HealingEffect(7))),
ROAST_RABBIT(new Food(new int[] {7223}, new HealingEffect(7))),
LEAN_SNAIL(new Food(new int[] {3371}, new HealingEffect(8))),
LEAN_SNAIL(new Food(new int[] {3371}, new RandomHealthEffect(6, 8))),
COD(new Food(new int[] {339}, new HealingEffect(7))),
PIKE(new Food(new int[] {351}, new HealingEffect(8))),
ROAST_BEAST_MEAT(new Food(new int[] {9988}, new HealingEffect(8))),
COOKED_CRAB_MEAT(new Food(new int[] {7521, 7523, 7524, 7525, 7526}, new HealingEffect(2))),
FAT_SNAIL(new Food(new int[] {3373}, new HealingEffect(9))),
FAT_SNAIL(new Food(new int[] {3373}, new RandomHealthEffect(7, 9))),
SALMON(new Food(new int[] {329}, new HealingEffect(9))),
SLIMY_EEL(new Food(new int[] {3381}, new HealingEffect(6))),
SLIMY_EEL(new Food(new int[] {3381}, new RandomHealthEffect(6, 10))),
TUNA(new Food(new int[] {361}, new HealingEffect(10))),
COOKED_KARAMBWAN(new Food(new int[] {3144}, new HealingEffect(18)), true),
COOKED_CHOMPY(new Food(new int[] {2878}, new HealingEffect(10))),
RAINBOW_FISH(new Food(new int[] {10136}, new HealingEffect(11))),
CAVE_EEL(new Food(new int[] {5003}, new HealingEffect(7))),
CAVE_EEL(new Food(new int[] {5003}, new RandomHealthEffect(8, 12))),
LOBSTER(new Food(new int[] {379}, new HealingEffect(12))),
COOKED_JUBBLY(new Food(new int[] {7568}, new HealingEffect(15))),
BASS(new Food(new int[] {365}, new HealingEffect(13))),
SWORDFISH(new Food(new int[] {373}, new HealingEffect(14))),
LAVA_EEL(new Food(new int[] {2149}, new HealingEffect(14))),
LAVA_EEL(new Food(new int[] {2149}, new HealingEffect(11))),
MONKFISH(new Food(new int[] {7946}, new HealingEffect(16))),
SHARK(new Food(new int[] {385}, new HealingEffect(20))),
SEA_TURTLE(new Food(new int[] {397}, new HealingEffect(21))),
MANTA_RAY(new Food(new int[] {391}, new HealingEffect(22))),
KARAMBWANJI(new Food(new int[] {3151}, new HealingEffect(3))),
STUFFED_SNAKE(new Food(new int[] {7579}, new HealingEffect(20), "You eat the stuffed snake-it's quite a meal! It tastes like chicken.")),
CRAYFISH(new Food(new int[] {13433}, new HealingEffect(2))),
CRAYFISH(new Food(new int[] {13433}, new HealingEffect(1))),
GIANT_FROG_LEGS(new Food(new int [] {4517}, new HealingEffect(6))),
/** Breads */
@ -111,7 +111,7 @@ public enum Consumables {
/** Vegetables */
POTATO(new Food(new int[] {1942}, new HealingEffect(1), "You eat the potato. Yuck!")),
BAKED_POTATO(new Food(new int[] {6701}, new HealingEffect(2))),
BAKED_POTATO(new Food(new int[] {6701}, new HealingEffect(4))),
SPICY_SAUCE(new Food(new int[] {7072, 1923}, new HealingEffect(2))),
CHILLI_CON_CARNE(new Food(new int[] {7062, 1923}, new HealingEffect(5))),
SCRAMBLED_EGG(new Food(new int[] {7078, 1923}, new HealingEffect(5))),
@ -128,8 +128,8 @@ public enum Consumables {
MUSHROOM_POTATO(new Food(new int[] {7058}, new HealingEffect(20))),
TUNA_AND_CORN(new Food(new int[] {7068, 1923}, new HealingEffect(13))),
TUNA_POTATO(new Food(new int[] {7060}, new HealingEffect(22))),
ONION(new Food(new int[] {1957}, new HealingEffect(2), "It's always sad to see a grown man/woman cry.")),
CABBAGE(new Food(new int[] {1965}, new HealingEffect(2), "You eat the cabbage. Yuck!")),
ONION(new Food(new int[] {1957}, new HealingEffect(1), "It's always sad to see a grown man/woman cry.")),
CABBAGE(new Food(new int[] {1965}, new HealingEffect(1), "You eat the cabbage. Yuck!")),
DRAYNOR_CABBAGE(new Food(new int[] {1967}, new DraynorCabbageEffect(), "You eat the cabbage.", "It seems to taste nicer than normal.")),
EVIL_TURNIP(new Food(new int[] {12134, 12136, 12138}, new HealingEffect(6))),
SPINACH_ROLL(new Food(new int[] {1969}, new HealingEffect(2))),
@ -147,7 +147,7 @@ public enum Consumables {
ORANGE(new Food(new int[] {2108}, new HealingEffect(2))),
ORANGE_CHUNKS(new Food(new int[] {2110}, new HealingEffect(2))),
ORANGE_SLICES(new Food(new int[] {2112}, new HealingEffect(2))),
PAPAYA_FRUIT(new Food(new int[] {5972}, new HealingEffect(2))),
PAPAYA_FRUIT(new Food(new int[] {5972}, new MultiEffect(new EnergyEffect(5), new HealingEffect(8)))),
TENTI_PINEAPPLE(new FakeConsumable(1851, new String[] {"Try using a knife to slice it into pieces."})),
PINEAPPLE(new FakeConsumable(2114, new String[] {"Try using a knife to slice it into pieces."})),
PINEAPPLE_CHUNKS(new Food(new int[] {2116}, new HealingEffect(2))),
@ -169,8 +169,8 @@ public enum Consumables {
STRANGE_FRUIT(new Food(new int[] {464}, new MultiEffect(new RemoveTimerEffect("poison"), new EnergyEffect(30)))),
/** Gnome Cooking */
TOAD_CRUNCHIES(new Food(new int[] {2217}, new HealingEffect(12))),
PREMADE_TD_CRUNCH(new Food(new int[] {2243}, new HealingEffect(12))),
TOAD_CRUNCHIES(new Food(new int[] {2217}, new HealingEffect(8))),
PREMADE_TD_CRUNCH(new Food(new int[] {2243}, new HealingEffect(8))),
SPICY_CRUNCHIES(new Food(new int[] {2213}, new HealingEffect(7))),
PREMADE_SY_CRUNCH(new Food(new int[] {2241}, new HealingEffect(7))),
WORM_CRUNCHIES(new Food(new int[] {2205}, new HealingEffect(8))),
@ -307,7 +307,7 @@ public enum Consumables {
OOMLIE_WRAP(new Food(new int[] {Items.COOKED_OOMLIE_WRAP_2343}, new MultiEffect(new HealingEffect(14), new AchievementEffect(DiaryType.KARAMJA, 2, 2)))),
ROE(new Food(new int[]{11324}, new HealingEffect(3))),
EQUA_LEAVES(new Food(new int[]{2128}, new HealingEffect(1))),
CHOC_ICE(new Food(new int[]{6794}, new HealingEffect(6))),
CHOC_ICE(new Food(new int[]{6794}, new HealingEffect(7))),
EDIBLE_SEAWEED(new Food(new int[] {403}, new HealingEffect(4))),
FROG_SPAWN(new Food(new int[] {5004}, new RandomHealthEffect(3, 7), "You eat the frogspawn. Yuck.")),
@ -319,44 +319,44 @@ public enum Consumables {
STRENGTH(new Potion(new int[] {113, 115, 117, 119}, new SkillEffect(Skills.STRENGTH, 3, 0.1))),
ATTACK(new Potion(new int[] {2428, 121, 123, 125}, new SkillEffect(Skills.ATTACK, 3, 0.1))),
DEFENCE(new Potion(new int[] {2432, 133, 135, 137}, new SkillEffect(Skills.DEFENCE, 3, 0.1))),
RANGING(new Potion(new int[] {2444, 169, 171, 173}, new SkillEffect(Skills.RANGE, 3, 0.1))),
MAGIC(new Potion(new int[] {3040, 3042, 3044, 3046}, new SkillEffect(Skills.MAGIC, 3, 0.1))),
SUPER_STRENGTH(new Potion(new int[] {2440, 157, 159, 161}, new SkillEffect(Skills.STRENGTH, 3, 0.2))),
SUPER_ATTACK(new Potion(new int[] {2436, 145, 147, 149}, new SkillEffect(Skills.ATTACK, 3, 0.2))),
SUPER_DEFENCE(new Potion(new int[] {2442, 163, 165, 167}, new SkillEffect(Skills.DEFENCE, 3, 0.2))),
RANGING(new Potion(new int[] {2444, 169, 171, 173}, new SkillEffect(Skills.RANGE, 4, 0.1))),
MAGIC(new Potion(new int[] {3040, 3042, 3044, 3046}, new SkillEffect(Skills.MAGIC, 4, 0))),
SUPER_STRENGTH(new Potion(new int[] {2440, 157, 159, 161}, new SkillEffect(Skills.STRENGTH, 5, 0.15))),
SUPER_ATTACK(new Potion(new int[] {2436, 145, 147, 149}, new SkillEffect(Skills.ATTACK, 5, 0.15))),
SUPER_DEFENCE(new Potion(new int[] {2442, 163, 165, 167}, new SkillEffect(Skills.DEFENCE, 5, 0.15))),
ANTIPOISON(new Potion(new int[] {2446, 175, 177, 179}, new AddTimerEffect("poison:immunity", secondsToTicks(90)))),
ANTIPOISON_(new Potion(new int[] {5943, 5945, 5947, 5949}, new AddTimerEffect("poison:immunity", minutesToTicks(9)))),
ANTIPOISON__(new Potion(new int[] {5952, 5954, 5956, 5958}, new AddTimerEffect("poison:immunity", minutesToTicks(12)))),
SUPER_ANTIP(new Potion(new int[] {2448, 181, 183, 185}, new AddTimerEffect("poison:immunity", minutesToTicks(6)))),
RELICYM(new Potion(new int[] {4842, 4844, 4846, 4848}, new MultiEffect(new SetAttributeEffect("disease:immunity", 300), new RemoveTimerEffect("disease")))),
RELICYM(new Potion(new int[] {4842, 4844, 4846, 4848}, new CureDiseaseEffect())),
AGILITY(new Potion(new int[] {3032, 3034, 3036, 3038}, new SkillEffect(Skills.AGILITY, 3, 0))),
HUNTER(new Potion(new int[] {9998, 10000, 10002, 10004}, new SkillEffect(Skills.HUNTER, 3, 0))),
RESTORE(new Potion(new int[] {2430, 127, 129, 131}, new RestoreEffect(10, 0.3))),
SARA_BREW(new Potion(new int[] {6685, 6687, 6689, 6691}, new MultiEffect(new PercentHeal(2, .15), new SkillEffect(Skills.ATTACK, 0, -0.10), new SkillEffect(Skills.STRENGTH, 0, -0.10), new SkillEffect(Skills.MAGIC, 0, -0.10), new SkillEffect(Skills.RANGE, 0, -0.10), new SkillEffect(Skills.DEFENCE, 2, 0.2)))),
SARA_BREW(new Potion(new int[] {6685, 6687, 6689, 6691}, new MultiEffect(new PercentHeal(0, .15), new SkillEffect(Skills.ATTACK, 0, -0.10), new SkillEffect(Skills.STRENGTH, 0, -0.10), new SkillEffect(Skills.MAGIC, 0, -0.10), new SkillEffect(Skills.RANGE, 0, -0.10), new SkillEffect(Skills.DEFENCE, 0, 0.25)))),
SUMMONING(new Potion(new int[] {12140, 12142, 12144, 12146}, new MultiEffect(new RestoreSummoningSpecial(), new SummoningEffect(7, 0.25)))),
COMBAT(new Potion(new int[] {9739, 9741, 9743, 9745}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, .1), new SkillEffect(Skills.ATTACK, 3, .1)))),
ENERGY(new Potion(new int[] {3008, 3010, 3012, 3014}, new MultiEffect(new EnergyEffect(10), new HealingEffect(3)))),
ENERGY(new Potion(new int[] {3008, 3010, 3012, 3014}, new EnergyEffect(10))),
FISHING(new Potion(new int[] {2438, 151, 153, 155}, new SkillEffect(Skills.FISHING, 3, 0))),
PRAYER(new Potion(new int[] {2434, 139, 141, 143}, new PrayerEffect(7, 0.25))),
SUPER_RESTO(new Potion(new int[] {3024, 3026, 3028, 3030}, new MultiEffect(new RestoreEffect(8, 0.25), new PrayerEffect(8, 0.25), new SummoningEffect(8, 0.25)))),
ZAMMY_BREW(new Potion(new int[] {2450, 189, 191, 193}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1), new RandomPrayerEffect(0, 10)))),
ANTIFIRE(new Potion(new int[] {2452, 2454, 2456, 2458}, new SetAttributeEffect("fire:immune", 600, true))),
SUPER_RESTO(new Potion(new int[] {3024, 3026, 3028, 3030}, new RestoreEffect(8, 0.25, true))),
ZAMMY_BREW(new Potion(new int[] {2450, 189, 191, 193}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.25), new SkillEffect(Skills.STRENGTH, 0, 0.15), new SkillEffect(Skills.DEFENCE, 0, -0.1)))),
ANTIFIRE(new Potion(new int[] {2452, 2454, 2456, 2458}, new AddTimerEffect("dragonfire:immunity", 600, true))),
GUTH_REST(new Potion(new int[] {4417, 4419, 4421, 4423}, new MultiEffect(new RemoveTimerEffect("poison"), new EnergyEffect(5), new HealingEffect(5)))),
MAGIC_ESS(new Potion(new int[] {11491, 11489}, new SkillEffect(Skills.MAGIC,3,0))),
SANFEW(new Potion(new int[] {10925, 10927, 10929, 10931}, new MultiEffect(new RestoreEffect(8,0.25), new PrayerEffect(8,0.25), new RemoveTimerEffect("poison"), new RemoveTimerEffect("disease")))),
SANFEW(new Potion(new int[] {10925, 10927, 10929, 10931}, new MultiEffect(new RestoreEffect(8,0.25, true), new AddTimerEffect("poison:immunity", secondsToTicks(90)), new RemoveTimerEffect("disease")))),
SUPER_ENERGY(new Potion(new int[] {3016, 3018, 3020, 3022}, new EnergyEffect(20))),
BLAMISH_OIL(new FakeConsumable(1582, new String[] {"You know... I'd really rather not."})),
/** Barbarian Mixes */
PRAYERMIX(new BarbarianMix(new int[] {11465, 11467}, new MultiEffect(new PrayerEffect(7, 0.25), new HealingEffect(6)))),
ZAMMY_MIX(new BarbarianMix(new int[] {11521, 11523}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1), new RandomPrayerEffect(0, 10)))),
ZAMMY_MIX(new BarbarianMix(new int[] {11521, 11523}, new MultiEffect(new DamageEffect(10, true), new SkillEffect(Skills.ATTACK, 0, 0.15), new SkillEffect(Skills.STRENGTH, 0, 0.25), new SkillEffect(Skills.DEFENCE, 0, -0.1)))),
ATT_MIX(new BarbarianMix(new int[] {11429, 11431}, new MultiEffect(new SkillEffect(Skills.ATTACK, 3, 0.1), new HealingEffect(3)))),
ANTIP_MIX(new BarbarianMix(new int[] {11433, 11435}, new MultiEffect(new AddTimerEffect("poison:immunity", secondsToTicks(90)), new HealingEffect(3)))),
RELIC_MIX(new BarbarianMix(new int[] {11437, 11439}, new MultiEffect(new RemoveTimerEffect("disease"), new SetAttributeEffect("disease:immunity", 300), new HealingEffect(3)))),
RELIC_MIX(new BarbarianMix(new int[] {11437, 11439}, new MultiEffect(new CureDiseaseEffect(), new HealingEffect(3)))),
STR_MIX(new BarbarianMix(new int[] {11443, 11441}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, 0.1), new HealingEffect(3)))),
RESTO_MIX(new BarbarianMix(new int[] {11449, 11451}, new MultiEffect(new RestoreEffect(10, 0.3), new HealingEffect(3)))),
SUPER_RESTO_MIX(new BarbarianMix(new int [] {11493, 11495}, new MultiEffect(new RestoreEffect(8,0.25), new PrayerEffect(8, 0.25), new SummoningEffect(8, 0.25), new HealingEffect(6)))),
ENERGY_MIX(new BarbarianMix(new int[] {11453, 11455}, new MultiEffect(new EnergyEffect(10), new HealingEffect(6)))),
ENERGY_MIX(new BarbarianMix(new int[] {11453, 11455}, new MultiEffect(new EnergyEffect(10), new HealingEffect(3)))),
DEF_MIX(new BarbarianMix(new int[] {11457, 11459}, new MultiEffect(new SkillEffect(Skills.DEFENCE, 3, 0.1), new HealingEffect(6)))),
AGIL_MIX(new BarbarianMix(new int[] {11461, 11463}, new MultiEffect(new SkillEffect(Skills.AGILITY, 3, 0), new HealingEffect(6)))),
COMBAT_MIX(new BarbarianMix(new int[] {11445, 11447}, new MultiEffect(new SkillEffect(Skills.ATTACK, 3, 0.1), new SkillEffect(Skills.STRENGTH, 3, 0.1), new HealingEffect(6)))),
@ -367,6 +367,7 @@ public enum Consumables {
SUPER_STR_MIX(new BarbarianMix(new int[] {11485, 11487}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 5, 0.15), new HealingEffect(6)))),
ANTIDOTE_PLUS_MIX(new BarbarianMix(new int[] {11501, 11503}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(9)), new RandomHealthEffect(3, 7)))),
ANTIP_SUPERMIX(new BarbarianMix(new int[] {11473, 11475}, new MultiEffect(new AddTimerEffect("poison:immunity", minutesToTicks(6)), new RandomHealthEffect(3, 7)))),
ANTIFIRE_MIX(new BarbarianMix(new int[] {11505, 11507}, new MultiEffect(new AddTimerEffect("dragonfire:immunity", 600, true), new RandomHealthEffect(3, 7)))),
/** Stealing creation potions */
SC_PRAYER(new Potion(new int[] {14207, 14209, 14211, 14213, 14215}, new PrayerEffect(7, 0.25))),

View file

@ -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<Disease>(p)
if (existingTimer != null) {
existingTimer.hitsLeft -= 9
if (existingTimer.hitsLeft <= 0) {
sendMessage(p, "The disease has been cured.")
removeTimer<Disease>(p)
}else{
sendMessage(p,"You feel slightly better.")
}
}
}
}

View file

@ -6,21 +6,37 @@ import core.game.node.entity.skill.Skills;
public class RestoreEffect extends ConsumableEffect {
double base,bonus;
boolean all_skills; // Except for hitpoints
public RestoreEffect(double base, double bonus){
this.base = base;
this.bonus = bonus;
this.all_skills = false;
}
public RestoreEffect(double base, double bonus, boolean all_skills){
this.base = base;
this.bonus = bonus;
this.all_skills = all_skills;
}
final int[] SKILLS = new int[] { Skills.DEFENCE, Skills.ATTACK, Skills.STRENGTH, Skills.MAGIC, Skills.RANGE };
final int[] ALL_SKILLS = new int[]{
Skills.ATTACK,Skills.DEFENCE, Skills.STRENGTH,Skills.RANGE,Skills.PRAYER,Skills.MAGIC, Skills.COOKING,
Skills.WOODCUTTING,Skills.FLETCHING,Skills.FISHING,Skills.FIREMAKING,Skills.CRAFTING,Skills.SMITHING,
Skills.MINING,Skills.HERBLORE,Skills.AGILITY,Skills.THIEVING,Skills.SLAYER,Skills.FARMING,
Skills.RUNECRAFTING,Skills.HUNTER,Skills.CONSTRUCTION,Skills.SUMMONING };
@Override
public void activate(Player p) {
Skills sk = p.getSkills();
for(int skill : SKILLS){
int[] skills = this.all_skills ? ALL_SKILLS : SKILLS;
for(int skill : skills){
int statL = sk.getStaticLevel(skill);
int boost = (int) (base + (statL * bonus));
int curL = sk.getLevel(skill);
if(curL < statL){
int boost = (int) (base + (statL * bonus));
p.getSkills().updateLevel(skill, boost, statL);
}
if (skill == Skills.PRAYER)
p.getSkills().incrementPrayerPoints(boost);
}
}
}

View file

@ -114,7 +114,6 @@ class ChampionChallengeListener : InteractionListener, MapArea {
)
private val PORTCULLIS = Scenery.PORTCULLIS_10553
private val LADDER = Scenery.LADDER_10554
private val CHAMPION_STATUE_CLOSED = Scenery.CHAMPION_STATUE_10556
private val CHAMPION_STATUE_OPEN = Scenery.CHAMPION_STATUE_10557
private val TRAPDOOR_CLOSED = Scenery.TRAPDOOR_10558
@ -124,6 +123,13 @@ class ChampionChallengeListener : InteractionListener, MapArea {
private val ARENA_ZONE = 12696
override fun defineListeners() {
// Champion's Guild Basement Ladder to Main Floor
addClimbDest(Location(3190, 9758, 0), Location(3190, 3356, 0))
// Champion Statue Ladder to Arena
addClimbDest(Location(3184, 9758, 0), Location(3182, 9758, 0))
// Arena Ladder to Champion's Guild Basement
addClimbDest(Location(3183, 9758, 0), Location(3185, 9758, 0))
on(LARXUS, IntType.NPC, "talk-to") { player, _ ->
openDialogue(player, LarxusDialogue(false))
return@on true
@ -134,18 +140,18 @@ class ChampionChallengeListener : InteractionListener, MapArea {
return@on true
}
on(TRAPDOOR_CLOSED, IntType.SCENERY, "open") { _, node ->
replaceScenery(node.asScenery(), TRAPDOOR_OPEN, 100, node.location)
return@on true
}
onUseWith(IntType.NPC, ChampionScrollsDropHandler.SCROLLS, NPCs.LARXUS_3050) { player, _, _ ->
openDialogue(player, LarxusDialogue(true))
return@onUseWith true
}
on(TRAPDOOR_CLOSED, IntType.SCENERY, "open") { _, node ->
replaceScenery(node.asScenery(), TRAPDOOR_OPEN, 100, node.location)
return@on true
}
on(TRAPDOOR_OPEN, IntType.SCENERY, "close") { _, node ->
replaceScenery(node.asScenery(), TRAPDOOR_CLOSED, 100, node.location)
replaceScenery(node.asScenery(), TRAPDOOR_CLOSED, -1, node.location)
return@on true
}
@ -154,15 +160,6 @@ class ChampionChallengeListener : InteractionListener, MapArea {
return@on true
}
on(LADDER, IntType.SCENERY, "climb-up") { player, _ ->
teleport(player, Location.create(3185, 9758, 0))
return@on true
}
on(CHAMPION_STATUE_OPEN, IntType.SCENERY, "climb-down") { player, _ ->
teleport(player, Location.create(3182, 9758, 0))
return@on true
}
on(PORTCULLIS, IntType.SCENERY, "open") { player, node ->
if (player.getAttribute("championsarena:start", false) == false) {
sendNPCDialogue(player, NPCs.LARXUS_3050, "You need to arrange a challenge with me before you enter the arena.")
@ -229,4 +226,4 @@ class ChampionChallengeListener : InteractionListener, MapArea {
ZoneRestriction.RANDOM_EVENTS
)
}
}
}

View file

@ -11,12 +11,13 @@ import core.ServerStore.Companion.getString
import content.global.bots.ShootingStarBot
import core.game.world.repository.Repository
import core.tools.RandomFunction
/**
* Represents a shooting star object (Only ever initialized once) (ideally)
* @author Ceikry
*/
class ShootingStar(var level: ShootingStarType = ShootingStarType.values().random()){
class ShootingStar(var level: ShootingStarType = ShootingStarType.values().random()) {
val crash_locations = mapOf(
"East of Dark Wizards' Tower" to Location.create(2925, 3339, 0), // East of Dark Wizards' Tower
"Crafting Guild" to Location.create(2940, 3280, 0), // Crafting Guild Mine
@ -26,12 +27,12 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
"Brimhaven mining site" to Location.create(2743, 3143, 0), // Brimhaven mining site
"South Crandor mining site" to Location.create(2822, 3239, 0), // South Crandor mining site (requires Dragon Slayer)
"Karamja mining site" to Location.create(2854, 3032, 0), // Karamja mining site
"Shilo Village mining site" to Location.create(2826, 2997, 0), // Shilo Village mining site/Gem rocks
"Shilo Village mining site" to Location.create(2826, 2997, 0), // Shilo Village mining site/Gem rocks (requires Shilo Village prereqs)
"Relleka mining site" to Location.create(2682, 3700, 0), // Rellekka mining site
"Jatizso mine" to Location.create(2393, 3815, 0), //Jatiszo mining site (requires Fremennik Trials)
//"Lunar Isle mine" to Location.create(2140, 3939, 0), // Lunar Isle mine (requires Lunar Diplomacy?)
//"Miscellania coal mine" to Location.create(2529, 3887, 0), // Miscellania coal mine (requires Fremennik Trials)
//"Neitiznot runite mine" to Location.create(2376, 3835, 0), // Near the Neitiznot runite mine (requires Fremennik Trials) currently inaccessible as bridge does not work
"Jatizso mine" to Location.create(2393, 3815, 0), //Jatiszo mining site (requires Fremennik Isles prereqs)
"Lunar Isle mine" to Location.create(2140, 3939, 0), // Lunar Isle mine (requires Lunar Diplomacy prereqs)
"Miscellania coal mine" to Location.create(2529, 3887, 0), // Miscellania coal mine (requires The Fremennik Trials)
//"Neitiznot runite mine" to Location.create(2376, 3835, 0), // Near the Neitiznot runite mine (requires Fremennik Isles prereqs) currently inaccessible as bridge does not work
"Ardougne mining site" to Location.create(2600, 3232, 0), // Ardougne mining site (Monastery)
"Ardougne eastern mine" to Location.create(2706, 3334, 0), // Ardougne mining site (Legends Guild)
"Kandarin Coal trucks" to Location.create(2589, 3485, 0), // Kandarin Coal trucks
@ -48,11 +49,11 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
"South-west Varrock mine" to Location.create(3176, 3362, 0), // South-west Varrock mine / Champion's Guild mine
"Varrock east bank" to Location.create(3259, 3407, 0), // Varrock east bank / Rune shop
"Lumbridge Swamp south-east mine" to Location.create(3227, 3150, 0), // Lumbridge Swamp south-east mine
//"Burgh de Rott bank" to Location.create(3500, 3219, 0), // Burgh de Rott bank (requires Quest to enter)
//"Burgh de Rott bank" to Location.create(3500, 3219, 0), // Burgh de Rott bank (requires quest to enter)
"Canifis Bank" to Location.create(3504, 3487, 0), // Canifis bank
"Mos Le'Harmless bank" to Location.create(3687, 2969, 0), // Mos Le'Harmless bank (requires Quest to enter but is currently accessible for Slayer)
"Mos Le'Harmless bank" to Location.create(3687, 2969, 0), // Mos Le'Harmless bank (requires quest to enter but is currently accessible for Slayer)
"Gnome stronghold Bank" to Location.create(2460, 3432, 0), // Gnome stronghold bank
"Lletya bank" to Location.create(2329, 3163, 0), // Lletya bank (requires Roving Elves?)
"Lletya bank" to Location.create(2329, 3163, 0), // Lletya bank (requires MEP1 prereqs)
"Piscatoris mining site" to Location.create(2336, 3636, 0), // Piscatoris mining site
"North Edgeville mining site" to Location.create(3101, 3569, 0), // Wilderness Steel mine / Zamorak mage mine
"Southern wilderness mine" to Location.create(3025, 3591, 0), // Wilderness skeleton mine
@ -79,22 +80,15 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
* Degrades a ShootingStar (or removes the starObject and spawns a Star Sprite if it's the last star)
*/
fun degrade() {
if(level.ordinal == 0){
selfBots.filter { it.isMining() }.forEach { it.sleep() }
SceneryBuilder.remove(starObject)
isSpawned = false
starSprite.location = starObject.location
starSprite.init()
spriteSpawned = true
ShootingStarPlugin.getStoreFile().clear()
if(level.ordinal == 0) {
spawnSprite()
return
}
level = getNextType()
maxDust = level.totalStardust
dustLeft = level.totalStardust
ShootingStarPlugin.getStoreFile()["level"] = level.ordinal
ShootingStarPlugin.getStoreFile()["isDiscovered"] = isDiscovered
ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft
val newStar = Scenery(level.objectId, starObject.location)
SceneryBuilder.replace(starObject, newStar)
@ -110,7 +104,6 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
*/
fun fire() {
SceneryBuilder.remove(starObject)
rebuildVars()
clearSprite()
SceneryBuilder.add(starObject)
if(!isSpawned) {
@ -123,37 +116,54 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
}
isSpawned = true
Repository.sendNews("A shooting star level ${level.ordinal + 1} just crashed near ${location}!")
ShootingStarPlugin.getStoreFile()["level"] = level.ordinal
ShootingStarPlugin.getStoreFile()["location"] = location
ShootingStarPlugin.getStoreFile()["isDiscovered"] = isDiscovered
ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft
}
/**
* Rebuilds some of the variables with new information.
*/
fun rebuildVars(){
if(firstStar && ShootingStarPlugin.getStoreFile().isNotEmpty()){
level = ShootingStarType.values()[ShootingStarPlugin.getStoreFile().getInt("level")]
location = ShootingStarPlugin.getStoreFile().getString("location")
isDiscovered = ShootingStarPlugin.getStoreFile().getBoolean("isDiscovered")
} else {
level = ShootingStarType.values().random()
location = crash_locations.entries.random().key
isDiscovered = false
// Defaults
var levelOrd = RandomFunction.random(9)
level = ShootingStarType.values()[levelOrd]
location = crash_locations.entries.random().key
isDiscovered = false
dustLeft = level.totalStardust
ticks = 0
spriteSpawned = false
if (firstStar && ShootingStarPlugin.getStoreFile().isNotEmpty()) {
// Replace default with stored values, if any
levelOrd = ShootingStarPlugin.getStoreFile().getInt("level", levelOrd)
level = ShootingStarType.values()[levelOrd]
location = ShootingStarPlugin.getStoreFile().getString("location", location)
isDiscovered = ShootingStarPlugin.getStoreFile().getBoolean("isDiscovered", false)
dustLeft = ShootingStarPlugin.getStoreFile().getInt("dustLeft", dustLeft)
ticks = ShootingStarPlugin.getStoreFile().getInt("ticks", ticks)
spriteSpawned = ShootingStarPlugin.getStoreFile().getBoolean("spriteSpawned", false)
}
maxDust = level.totalStardust
dustLeft = level.totalStardust
starObject = Scenery(level.objectId, crash_locations.get(location))
}
ShootingStarPlugin.getStoreFile()["level"] = level.ordinal
ShootingStarPlugin.getStoreFile()["location"] = location
ShootingStarPlugin.getStoreFile()["isDiscovered"] = false
ticks = 0
firstStar = false
fun spawnSprite() {
selfBots.filter { it.isMining() }.forEach { it.sleep() }
SceneryBuilder.remove(starObject)
isSpawned = false
starSprite.location = starObject.location
starSprite.init()
spriteSpawned = true
ShootingStarPlugin.getStoreFile()["spriteSpawned"] = spriteSpawned
}
fun clearSprite() {
starSprite.clear()
spriteSpawned = false
ShootingStarPlugin.getStoreFile()["spriteSpawned"] = spriteSpawned
}
/**
@ -161,6 +171,7 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando
*/
fun decDust() {
if(--dustLeft <= 0) degrade()
ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft
}
/**

View file

@ -51,6 +51,7 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin
player.sendMessage("You have ${player.skills.experienceMultiplier * player.getAttribute("shooting-star:bonus-xp", 0).toDouble()} bonus xp towards mining stardust.")
ShootingStarPlugin.submitScoreBoard(player)
star.isDiscovered = true
ShootingStarPlugin.getStoreFile()["isDiscovered"] = star.isDiscovered
return player.skills.getLevel(Skills.MINING) >= star.miningLevel
}

View file

@ -12,11 +12,11 @@ import core.ServerStore.Companion.getBoolean
import core.game.dialogue.DialogueFile
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import core.tools.SystemLogger
import core.game.system.command.Privilege
import core.game.world.GameWorld
import core.tools.Log
import core.tools.secondsToTicks
import content.data.Quests
class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Commands, StartupListener {
override fun login(player: Player) {
@ -27,14 +27,30 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
override fun tick() {
++star.ticks
// Check if the current star sprite should expire
val maxDelay = tickDelay + (tickDelay / 3)
if(star.ticks > maxDelay && star.spriteSpawned){
star.clearSprite()
}
if ((star.ticks >= tickDelay && !star.spriteSpawned) || (!star.isSpawned && !star.spriteSpawned)) {
if (star.firstStar && !star.isSpawned && !star.spriteSpawned) {
// Apparently, the server has only just booted
star.rebuildVars()
if (star.spriteSpawned) {
star.spawnSprite()
} else {
star.fire()
}
star.firstStar = false
}
// Check if it's time to fire a new one
if (star.ticks >= tickDelay && !star.spriteSpawned) {
star.rebuildVars()
star.fire()
}
getStoreFile()["ticks"] = star.ticks
}
override fun defineListeners() {
@ -66,35 +82,52 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
return@on true
}
val condition: (Player) -> Boolean = when(star.location.toLowerCase()){
"canifis bank" -> { p -> hasRequirement(p, "Priest in Peril") }
//"Burgh de Rott bank" -> { p -> p.questRepository.isComplete("Priest in Peril")} // for now, require this as it is in Morytania. remove when appropriate quests added - Crash
"crafting guild" -> {p -> hasLevelStat(p, Skills.CRAFTING, 40) }
"lletya bank" -> {p -> hasRequirement(p, "Mourning's End Part I") }
"jatizso mine" -> {p -> hasRequirement(player, "Fremennik Trials") }
"south crandor mining site" -> {p -> hasRequirement(p, "Dragon Slayer") }
"shilo village mining site" -> {p -> hasRequirement(p, "Shilo Village") }
"mos le'harmless bank" -> {p -> hasRequirement(p, "Cabin Fever") }
else -> {_ -> true}
class RingDialogue(val star: ShootingStar) : DialogueFile() {
val shouldWarn = when (star.location) {
"North Edgeville mining site",
"Southern wilderness mine",
"Wilderness hobgoblin mine",
"Pirates' Hideout mine",
"Lava Maze mining site",
"Mage Arena bank" -> true
else -> false
}
override fun handle(componentID: Int, buttonID: Int) {
fun teleportToStar(player: Player) {
val condition: (p: Player) -> Boolean = when (star.location.toLowerCase()) {
"canifis bank" -> {p -> requireQuest(p, Quests.PRIEST_IN_PERIL, "to access this.")}
//"burgh de rott bank" -> {p -> hasRequirement(p, Quests.IN_AID_OF_THE_MYREQUE)} //disabled: crash
"crafting guild" -> {p -> hasLevelStat(p, Skills.CRAFTING, 40)}
"lletya bank" -> {p -> hasRequirement(p, Quests.MOURNINGS_END_PART_I)}
"jatizso mine" -> {p -> hasRequirement(p, Quests.THE_FREMENNIK_ISLES)}
"south crandor mining site" -> {p -> hasRequirement(p, Quests.DRAGON_SLAYER)}
"shilo village mining site" -> {p -> hasRequirement(p, Quests.SHILO_VILLAGE)}
"mos le'harmless bank" -> {p -> hasRequirement(p, Quests.CABIN_FEVER)} //needs to be updated to check for completion when the quest releases; https://runescape.wiki/w/Mos_Le%27Harmless?oldid=913025
"lunar isle mine" -> {p -> hasRequirement(p, Quests.LUNAR_DIPLOMACY)}
"miscellania coal mine" -> {p -> requireQuest(p, Quests.THE_FREMENNIK_TRIALS, "to access this.")}
//"neitiznot runite mine" -> {p -> hasRequirement(p, Quests.THE_FREMENNIK_ISLES)} //disabled: currently not reachable
else -> {_ -> true}
}
if (!condition.invoke(player)) {
sendDialogue(player,"Magical forces prevent your teleportation.")
} else if (teleport(player, star.crash_locations[star.location]!!.transform(0, -1, 0), TeleportManager.TeleportType.MINIGAME)) {
getRingStoreFile()[player.username.toLowerCase()] = true
}
}
when (stage) {
0 -> dialogue(if (star.spriteSpawned) "The star sprite has already been freed." else "The star sprite is still trapped.").also { if (shouldWarn) stage++ else stage += 2 }
1 -> dialogue("WARNING: The star is located in the wilderness.").also { stage++ }
2 -> player.dialogueInterpreter.sendOptions("Teleport to the star?", "Yes", "No").also { stage++ }
3 -> when (buttonID) {
1 -> end().also { teleportToStar(player) }
2 -> end()
}
}
}
}
if(!condition.invoke(player)){
sendDialogue(player, "Magical forces prevent your teleportation.")
return@on true
}
val shouldWarn = when(star.location){
"North Edgeville mining site",
"Southern wilderness mine",
"Wilderness hobgoblin mine",
"Pirates' Hideout mine",
"Lava Maze mining site",
"Mage Arena bank" -> true
else -> false
}
openDialogue(player, RingDialogue(shouldWarn, star))
openDialogue(player, RingDialogue(star))
return@on true
}
}
@ -105,6 +138,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
}
define("submit", Privilege.ADMIN) { _, _ ->
star.rebuildVars()
star.fire()
}
@ -119,26 +153,6 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com
private data class ScoreboardEntry(val player: String, val time: Int)
private class RingDialogue(val shouldWarn: Boolean, val star: ShootingStar) : DialogueFile(){
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
0 -> dialogue(if (star.spriteSpawned) "The star sprite has already been freed." else "The star sprite is still trapped.").also { if (shouldWarn) stage++ else stage += 2 }
1 -> dialogue("WARNING: The star is located in the wilderness.").also { stage++ }
2 -> player!!.dialogueInterpreter.sendOptions("Teleport to the star?", "Yes", "No").also { stage++ }
3 -> when (buttonID) {
1 -> teleport(player!!, star).also { end() }
2 -> end()
}
}
}
fun teleport(player: Player, star: ShootingStar){
if (teleport(player, star.crash_locations[star.location]!!.transform(0, -1, 0), TeleportManager.TeleportType.MINIGAME)) {
getRingStoreFile()[player.username.toLowerCase()] = true
}
}
}
companion object {
private val star = ShootingStar()
private val tickDelay = if(GameWorld.settings?.isDevMode == true) 200 else 25000

View file

@ -192,7 +192,7 @@ public abstract class ClueScrollPlugin extends MapZone implements Plugin<Object>
int hasAmt = 0;
for (int i = 0; i < equipment.length; i++) {
for (int k = 0; k < equipment[i].length; k++) {
if (player.getEquipment().contains(equipment[i][k], 1)) {
if (player.getEquipment().contains(equipment[i][k], 1) || player.costumeRoomBank.contains(equipment[i][k], 1)) {
hasAmt++;
break;
}

View file

@ -0,0 +1,28 @@
package content.global.ame
import core.ServerConstants
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager.TeleportType
import core.game.world.map.Location
fun kidnapPlayer(player: Player, loc: Location, type: TeleportType) {
setAttribute(player, "kidnapped-by-random", true)
if (getAttribute(player, "/save:original-loc", null) == null) {
setAttribute(player, "/save:original-loc", player.location)
}
teleport(player, loc, type)
}
fun returnPlayer(player: Player) {
// Prevent returning more than once and sending the player back to HOME_LOCATION
if (getAttribute<Location?>(player, "kidnapped-by-random", null) == null) {
return
}
player.locks.unlockTeleport()
val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION ?: Location.create(3222, 3218, 0))
teleport(player, destination)
unlock(player)
removeAttributes(player, "/save:original-loc", "kidnapped-by-random")
}

View file

@ -3,9 +3,15 @@ package content.global.ame
import content.global.ame.events.MysteriousOldManNPC
import core.api.playGlobalAudio
import core.api.poofClear
import core.api.sendMessage
import core.api.utils.WeightBasedTable
import core.game.interaction.MovementPulse
import core.game.node.entity.Entity
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.impl.PulseType
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.agg.AggressiveBehavior
import core.game.node.entity.npc.agg.AggressiveHandler
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location
@ -13,10 +19,11 @@ import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Graphics
import core.integrations.discord.Discord
import core.api.utils.WeightBasedTable
import core.tools.secondsToTicks
import core.tools.ticksToCycles
import org.rs09.consts.Sounds
import kotlin.math.ceil
import kotlin.math.min
import kotlin.random.Random
import kotlin.reflect.full.createInstance
@ -66,7 +73,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
if (!player.getAttribute("random:pause", false)) {
ticksLeft--
}
if (!pulseManager.hasPulseRunning() && !finalized) {
follow()
}
@ -87,6 +93,11 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
location = spawnLocation
player.setAttribute("re-npc", this)
super.init()
super.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() {
override fun canSelectTarget(entity: Entity, target: Entity): Boolean {
return target == player
}
})
}
open fun onTimeUp() {
@ -118,4 +129,19 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
}
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<Int>, player: Player): Int {
val index = min(ids.size, ceil(player.properties.currentCombatLevel / 20.0).toInt()) - 1
return ids[index]
}
}

View file

@ -3,15 +3,18 @@ package content.global.ame
import org.rs09.consts.Items
import content.global.ame.events.MysteriousOldManNPC
import content.global.ame.events.certer.CerterNPC
import content.global.ame.events.drilldemon.SeargentDamienNPC
import content.global.ame.events.drilldemon.SergeantDamienNPC
import content.global.ame.events.drunkendwarf.DrunkenDwarfNPC
import content.global.ame.events.evilbob.EvilBobNPC
import content.global.ame.events.evilchicken.EvilChickenNPC
import content.global.ame.events.freakyforester.FreakyForesterNPC
import content.global.ame.events.maze.MazeNPC
import content.global.ame.events.genie.GenieNPC
import content.global.ame.events.pillory.PilloryNPC
import content.global.ame.events.rickturpentine.RickTurpentineNPC
import content.global.ame.events.rivertroll.RiverTrollRENPC
import content.global.ame.events.rockgolem.RockGolemRENPC
import content.global.ame.events.quizmaster.QuizMasterNPC
import content.global.ame.events.sandwichlady.SandwichLadyRENPC
import content.global.ame.events.shade.ShadeRENPC
import content.global.ame.events.strangeplant.StrangePlantNPC
@ -43,7 +46,8 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985,1,1,0.1),
WeightedItem(Items.LOOP_HALF_OF_A_KEY_987,1,1,0.1)
)),
DRILL_DEMON(npc = SeargentDamienNPC()),
MAZE(npc = MazeNPC()),
DRILL_DEMON(npc = SergeantDamienNPC()),
EVIL_CHICKEN(npc = EvilChickenNPC()),
STRANGE_PLANT(npc = StrangePlantNPC()),
SWARM(npc = SwarmNPC()),
@ -52,7 +56,9 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
RICK_TURPENTINE(npc = RickTurpentineNPC(), loot = CERTER.loot),
SURPRISE_EXAM(npc = MysteriousOldManNPC(), type = "sexam"),
FREAKY_FORESTER(npc = FreakyForesterNPC(), skillIds = intArrayOf(Skills.WOODCUTTING)),
PILLORY(npc = PilloryNPC(), skillIds = intArrayOf(Skills.THIEVING)),
TREE_SPIRIT(npc = TreeSpiritRENPC(), skillIds = intArrayOf(Skills.WOODCUTTING)),
QUIZ_MASTER(npc = QuizMasterNPC()),
RIVER_TROLL(RiverTrollRENPC(), skillIds = intArrayOf(Skills.FISHING)),
ROCK_GOLEM(RockGolemRENPC(), skillIds = intArrayOf(Skills.MINING)),
SHADE(ShadeRENPC(), skillIds = intArrayOf(Skills.PRAYER)),

View file

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

View file

@ -79,7 +79,7 @@ class DrillDemonListeners : InteractionListener, MapArea {
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS)
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP)
}
override fun areaEnter(entity: Entity) {
@ -90,4 +90,4 @@ class DrillDemonListeners : InteractionListener, MapArea {
setComponentVisibility(entity.asPlayer(), 746, 12, true)
}
}
}
}

View file

@ -1,8 +1,11 @@
package content.global.ame.events.drilldemon
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.update.flag.context.Animation
@ -11,7 +14,6 @@ import org.rs09.consts.NPCs
object DrillDemonUtils {
val DD_KEY_TASK = "/save:drilldemon:task"
val DD_KEY_RETURN_LOC = "/save:original-loc"
val DD_SIGN_VARP = 531
val DD_SIGN_JOG = 0
val DD_SIGN_SITUP = 1
@ -23,8 +25,7 @@ object DrillDemonUtils {
val DD_NPC = NPCs.SERGEANT_DAMIEN_2790
fun teleport(player: Player) {
setAttribute(player, DD_KEY_RETURN_LOC, player.location)
teleport(player, Location.create(3163, 4819, 0))
kidnapPlayer(player, Location.create(3163, 4819, 0), TeleportManager.TeleportType.INSTANT)
player.interfaceManager.closeDefaultTabs()
setComponentVisibility(player, 548, 69, true)
setComponentVisibility(player, 746, 12, true)
@ -63,13 +64,8 @@ object DrillDemonUtils {
}
fun cleanup(player: Player) {
player.locks.unlockTeleport()
unlock(player)
teleport(player, getAttribute(player, DD_KEY_RETURN_LOC, Location.create(3222, 3218, 0)))
removeAttribute(player, DD_KEY_RETURN_LOC)
removeAttribute(player, DD_KEY_TASK)
removeAttribute(player, DD_CORRECT_OFFSET)
removeAttribute(player, DD_CORRECT_COUNTER)
returnPlayer(player)
removeAttributes(player, DD_KEY_TASK, DD_CORRECT_OFFSET, DD_CORRECT_COUNTER)
player.interfaceManager.openDefaultTabs()
setComponentVisibility(player, 548, 69, false)
setComponentVisibility(player, 746, 12, false)

View file

@ -10,11 +10,11 @@ import core.game.interaction.QueueStrength
import core.game.system.timer.impl.AntiMacro
import core.tools.secondsToTicks
class SeargentDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) {
class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) {
override fun init() {
super.init()
sendChat(player.username.capitalize() + "! Drop and give me 20!")
sendChat(player.username+ "! Drop and give me 20!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {

View file

@ -1,5 +1,6 @@
package content.global.ame.events.evilbob
import content.global.ame.returnPlayer
import core.ServerConstants
import core.api.*
import core.game.dialogue.FacialExpression
@ -8,8 +9,6 @@ import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import core.game.node.entity.Entity
import core.game.node.entity.player.link.emote.Emotes
import core.game.node.entity.skill.Skills
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
@ -121,7 +120,7 @@ class EvilBobListeners : InteractionListener, MapArea {
}
3 -> {
sendMessage(player, "Welcome back to ${ServerConstants.SERVER_NAME}.")
teleport(player, getAttribute(player, EvilBobUtils.prevLocation, Location.create(3222, 3219, 0)))
returnPlayer(player)
EvilBobUtils.reward(player)
EvilBobUtils.cleanup(player)
resetAnimator(player)
@ -139,7 +138,7 @@ class EvilBobListeners : InteractionListener, MapArea {
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS)
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP)
}
override fun areaEnter(entity: Entity) {

View file

@ -1,7 +1,10 @@
package content.global.ame.events.evilbob
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.node.entity.skill.Skills
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
@ -13,10 +16,8 @@ import org.rs09.consts.NPCs
import org.rs09.consts.Scenery
object EvilBobUtils {
const val prevLocation = "/save:original-loc"
const val eventComplete = "/save:evilbob:eventcomplete"
const val assignedFishingZone = "/save:evilbob:fishingzone"
const val fishCaught = "evilbob:fishcaught"
const val attentive = "/save:evilbob:attentive"
const val servantHelpDialogueSeen = "/save:evilbob:servantdialogeseen"
const val attentiveNewSpot = "/save:evilbob:attentivenewspot"
@ -53,14 +54,11 @@ object EvilBobUtils {
}
fun teleport(player: Player) {
setAttribute(player, prevLocation, player.location)
player.properties.teleportLocation = Location.create(3419, 4776, 0)
kidnapPlayer(player, Location.create(3419, 4776, 0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player) {
player.locks.unlockTeleport()
player.properties.teleportLocation = getAttribute(player, prevLocation, null)
removeAttributes(player, assignedFishingZone, eventComplete, prevLocation, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen)
removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen)
removeAll(player, Items.FISHLIKE_THING_6202)
removeAll(player, Items.FISHLIKE_THING_6202, Container.BANK)
removeAll(player, Items.FISHLIKE_THING_6206)

View file

@ -9,9 +9,8 @@ import core.tools.RandomFunction
import org.rs09.consts.Items
import content.global.ame.RandomEventNPC
import core.api.utils.WeightBasedTable
import java.lang.Integer.max
val ids = 2463..2468
val ids = (2463..2468).toList()
class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(2463) {
val phrases = arrayOf("Bwuk","Bwuk bwuk bwuk","Flee from me, @name!","Begone, @name!","Bwaaaauuuk bwuk bwuk","MUAHAHAHAHAAA!")
@ -19,8 +18,7 @@ class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventN
override fun init() {
super.init()
val index = max(0, (player.properties.combatLevel / 20) - 1)
val id = ids.toList()[index]
val id = idForCombatLevel(ids, player)
this.transform(id)
this.attack(player)
sendChat(phrases.random().replace("@name",player.username.capitalize()))

View file

@ -57,10 +57,10 @@ class FreakListeners : InteractionListener, MapArea {
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS)
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP)
}
override fun areaEnter(entity: Entity) {
entity.locks.lockTeleport(1000000)
}
}
}

View file

@ -1,16 +1,18 @@
package content.global.ame.events.freakyforester
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.tools.RandomFunction
object FreakUtils{
const val freakNpc = NPCs.FREAKY_FORESTER_2458
const val freakPreviousLoc = "/save:original-loc"
const val freakTask = "/save:freakyf:task"
const val freakComplete = "/save:freakyf:complete"
const val pheasantKilled = "freakyf:killed"
@ -27,14 +29,12 @@ object FreakUtils{
}
fun teleport(player: Player) {
setAttribute(player, freakPreviousLoc, player.location)
teleport(player, Location.create(2599, 4777 ,0))
kidnapPlayer(player, Location.create(2599, 4777 ,0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player) {
player.locks.unlockTeleport()
player.properties.teleportLocation = getAttribute(player,freakPreviousLoc,null)
removeAttributes(player, freakPreviousLoc, freakTask, freakComplete, pheasantKilled)
returnPlayer(player)
removeAttributes(player, freakTask, freakComplete, pheasantKilled)
removeAll(player, Items.RAW_PHEASANT_6178)
removeAll(player, Items.RAW_PHEASANT_6178, Container.BANK)
removeAll(player, Items.RAW_PHEASANT_6179)

View file

@ -0,0 +1,277 @@
package content.global.ame.events.maze
import content.global.ame.returnPlayer
import core.api.*
import core.api.utils.WeightBasedTable
import core.api.utils.WeightedItem
import core.game.event.EventHook
import core.game.event.TickEvent
import core.game.global.action.DoorActionHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.GameWorld.Pulser
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.*
class MazeInterface : InteractionListener, EventHook<TickEvent>, MapArea {
companion object {
const val MAZE_TIMER_INTERFACE = Components.MAZETIMER_209
const val MAZE_TIMER_VARP = 531 // Interface 209 child 2 config: [531, 0]
const val MAZE_ATTRIBUTE_TICKS_LEFT = "maze:percent-ticks-left"
const val MAZE_ATTRIBUTE_CHESTS_OPEN = "/save:maze:chests-opened"
val STARTING_POINTS = arrayOf(
Location(2928, 4553, 0),
Location(2917, 4553, 0),
Location(2908, 4555, 0),
Location(2891, 4589, 0),
Location(2891, 4595, 0),
Location(2891, 4595, 0),
Location(2926, 4597, 0),
Location(2931, 4597, 0),
// There's 2 more, but there isn't a door for them...
)
val REWARD_ITEM = intArrayOf(
Items.COINS_995,
Items.FEATHER_314,
Items.IRON_ARROW_884,
Items.CHAOS_RUNE_562,
Items.STEEL_ARROW_886,
Items.DEATH_RUNE_560,
Items.COAL_454,
Items.NATURE_RUNE_561,
Items.MITHRIL_ORE_448
)
val ITEM_DIVISOR = arrayOf(
1.0,
2.0,
3.0,
9.0,
12.0,
18.0,
45.0,
162.0,
180.0,
)
val CHEST_REWARDS = WeightBasedTable.create(
WeightedItem(Items.AIR_RUNE_556,15,15,1.0),
WeightedItem(Items.WATER_RUNE_555,10,10,1.0),
WeightedItem(Items.EARTH_RUNE_557,10,10,1.0),
WeightedItem(Items.FIRE_RUNE_554,10,10,1.0),
WeightedItem(Items.BRONZE_ARROW_882,20,20,1.0),
WeightedItem(Items.BRONZE_BOLTS_877,10,10,1.0),
WeightedItem(Items.IRON_ARROW_884,15,15,1.0),
WeightedItem(Items.ATTACK_POTION2_123,1,1,1.0),
WeightedItem(Items.STRENGTH_POTION2_117,1,1,1.0),
WeightedItem(Items.DEFENCE_POTION2_135,1,1,1.0),
)
fun initMaze(player: Player) {
setAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 300)
setVarp(player, MAZE_TIMER_VARP, (getAttribute<Int>(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) / 3),false)
openOverlay(player, MAZE_TIMER_INTERFACE)
sendMessage(player, "You need to reach the maze center, then you'll be returned to where you were.")
sendNPCDialogue(player, NPCs.MYSTERIOUS_OLD_MAN_410, "You need to reach the maze center, then you'll be returned to where you were.")
}
fun calculateLoot(player: Player) {
val randomNumber = (0..8).random()
val totalLevel = player.getSkills().totalLevel.toDouble()
val rewardPotential = getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0).toDouble() / 300.0
val itemDivisor = ITEM_DIVISOR[randomNumber]
val itemQuantity = (totalLevel * rewardPotential * 3.33) / itemDivisor
// sendMessage(player, "Maze reward calculation: $totalLevel * $rewardPotential * 3.33 / $itemDivisor = $itemQuantity")
if (itemQuantity.toInt() > 0) {
addItemOrDrop(player, REWARD_ITEM[randomNumber], itemQuantity.toInt())
}
}
/**
* Chest Location to rotation mapping.
* This is needed as it is impossible to obtain the underlying chest scenery for the rotation.
* 0: Facing North, 1: Facing East, 2: Facing South, 3: Facing West
*/
val chestLocationRotationMap = mapOf(
Location(2930, 4595, 0).toString() to 2,
Location(2924, 4572, 0).toString() to 2,
Location(2925, 4573, 0).toString() to 0,
Location(2900, 4578, 0).toString() to 2,
Location(2901, 4560, 0).toString() to 1,
Location(2890, 4599, 0).toString() to 2,
Location(2896, 4591, 0).toString() to 2,
Location(2895, 4592, 0).toString() to 1,
Location(2901, 4560, 0).toString() to 3,
Location(2918, 4590, 0).toString() to 1,
Location(2917, 4590, 0).toString() to 3,
)
/**
* Chest Interaction workaround
*
* The issue here is that the walls(3626) of the Maze are overlapping some(not all) chest sceneries.
*
* The types for the wallScenery:
* Type 0 - flat panel |
* Type 2 - right angle panel with rotation 0:r 1:7 2:> 3:L
* Type 3 - corner post . for angle edges of walls
*/
fun overrideScenery(wallScenery: core.game.node.scenery.Scenery, chestSceneryId: Int): core.game.node.scenery.Scenery {
if (wallScenery.id == chestSceneryId) {
replaceScenery(wallScenery, Scenery.CHEST_3636, 30)
wallScenery.isActive = true
return wallScenery // Return the chest scenery as the wallScenery isn't there.
}
addScenery(Scenery.CHEST_3636, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10)
addScenery(wallScenery)
// replaceScenery(newChestScenery, Scenery.CHEST_3636, 3) // didn't work for an underlying scenery
// I did a world pulse since everyone will get to see the chest open.
Pulser.submit(object : Pulse(30) {
override fun pulse(): Boolean {
addScenery(Scenery.CHEST_3635, wallScenery.location, chestLocationRotationMap[wallScenery.location.toString()] ?: 0, 10)
addScenery(wallScenery)
return true
}
})
// Return the chest scenery to replace PacketProcessor so that MISMATCH will not happen.
return core.game.node.scenery.Scenery(
chestSceneryId,
wallScenery.location,
chestLocationRotationMap[wallScenery.location.toString()] ?: 0
)
}
}
override fun defineListeners() {
// This somehow doesn't trigger as the scenery.id != objId (3626 != 3635)
on(Scenery.CHEST_3635, IntType.SCENERY, "open") { player, node ->
if (getAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0 && getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0) < 10) {
animate(player, 536)
// val actualScenery = RegionManager.getObject(node.location.z, node.location.x, node.location.y, 3626)
val tableRoll = CHEST_REWARDS.roll()
addItemOrBank(player, tableRoll[0].id)
when (tableRoll[0].id){
Items.AIR_RUNE_556 -> sendItemDialogue(player, Items.AIR_RUNE_556, "You've found some air runes!")
Items.WATER_RUNE_555 -> sendItemDialogue(player, Items.WATER_RUNE_555, "You've found some water runes!")
Items.EARTH_RUNE_557 -> sendItemDialogue(player, Items.EARTH_RUNE_557, "You've found some earth runes!")
Items.FIRE_RUNE_554 -> sendItemDialogue(player, Items.FIRE_RUNE_554, "You've found some fire runes!")
Items.BRONZE_ARROW_882 -> sendItemDialogue(player, Items.BRONZE_ARROW_882, "You've found some bronze arrows!")
Items.BRONZE_BOLTS_877 -> sendItemDialogue(player, Items.BRONZE_BOLTS_877, "You've found some bronze bolts!")
Items.IRON_ARROW_884 -> sendItemDialogue(player, Items.IRON_ARROW_884, "You've found some iron arrows!")
Items.ATTACK_POTION2_123 -> sendItemDialogue(player, Items.ATTACK_POTION2_123, "You've found an attack potion!")
Items.STRENGTH_POTION2_117 -> sendItemDialogue(player, Items.STRENGTH_POTION2_117, "You've found a strength potion!")
Items.DEFENCE_POTION2_135 -> sendItemDialogue(player, Items.DEFENCE_POTION2_135, "You've found a defence potion!")
}
setAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, getAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN, 0))
} else {
sendMessage(player,"You find nothing of interest.")
}
return@on true
}
on(Scenery.CHEST_3636, SCENERY, "search") { player, node ->
sendMessage(player,"You find nothing of interest.")
return@on true
}
on(Scenery.WALL_3626, IntType.SCENERY, "open") { player, node ->
sendMessage(player, "That bit doesn't open.") // 0xBrLo9woIY
return@on true
}
on(Scenery.WALL_3628, IntType.SCENERY, "open") { player, node ->
// Door opening workaround
// Ignore 3629(WALL_3629) and 3630(WALL_3630) in handleAutowalkDoor ignoreSecondDoor
DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery)
return@on true
}
on(Scenery.STRANGE_SHRINE_3634, IntType.SCENERY, "touch") { player, node ->
player.unhook(this)
closeOverlay(player)
queueScript(player, 0, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(86, 0, 3), player.location)
animate(player,862)
return@queueScript delayScript(player, 6)
}
1 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
2 -> {
returnPlayer(player)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
closeOverlay(player)
return@queueScript delayScript(player, 1)
}
3 -> {
calculateLoot(player)
removeAttribute(player, MAZE_ATTRIBUTE_TICKS_LEFT)
removeAttribute(player, MAZE_ATTRIBUTE_CHESTS_OPEN)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
}
return@on true
}
}
override fun process(entity: Entity, event: TickEvent) {
if (entity is Player) {
if (getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) > 0) {
setAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) - 1)
}
setVarp(entity, MAZE_TIMER_VARP, (getAttribute(entity, MAZE_ATTRIBUTE_TICKS_LEFT, 0) / 3), false)
}
}
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(getRegionBorders(11591))
}
override fun getRestrictions(): Array<ZoneRestriction> {
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)
}
}
}

View file

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

View file

@ -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 <col=8A0808>swinging key</col> that matches the", "hole in the <col=8A0808>spinning lock</col>.")
} else {
sendMessage(player, "You can't unlock the pillory, you'll let all the prisoners out!")
}
return@on true
}
}
override fun defineAreaBorders(): Array<ZoneBorders> {
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<ZoneRestriction> {
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()
}
}
}

View file

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

View file

@ -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<ZoneBorders> {
return arrayOf(getRegionBorders(7754))
}
override fun getRestrictions(): Array<ZoneRestriction> {
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)
}
}
}

View file

@ -0,0 +1,129 @@
package content.global.ame.events.quizmaster
import content.global.ame.returnPlayer
import core.ServerConstants
import core.api.*
import core.api.utils.WeightBasedTable
import core.api.utils.WeightedItem
import core.game.dialogue.DialogueFile
import core.game.dialogue.FacialExpression
import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player
import core.tools.END_DIALOGUE
import org.rs09.consts.Components
import org.rs09.consts.Items
class QuizMasterDialogueFile : DialogueFile() {
companion object {
const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191
const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct"
const val QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER = "quizmaster:random-answer"
/*
// Golden Models:
8828 ADAMANT_BATTLEAXE_1371
8829 SALMON_329
8830 TROUT_333
8831 NECKLACE
8832 WOODEN_SHIELD_1171
8833 BRONZE_MED_HELM_1139
8834 RING
8835 SECATEURS_5329
8836 BRONZE_SWORD_1277
8837 GARDENING_TROWEL_5325
*/
val sets = arrayOf(
intArrayOf(8828, 8829, 8829),
intArrayOf(8831, 8837, 8835),
intArrayOf(8830, 8832, 8833),
intArrayOf(8835, 8834, 8831),
intArrayOf(8837, 8836, 8828),
)
fun randomQuestion(player: Player): Int {
val randomSet = intArrayOf(*sets.random())
val answer = intArrayOf(*randomSet)[0]
randomSet.shuffle()
val correctButton = randomSet.indexOf(answer) + 2 // buttons are 3,4,5
player.packetDispatch.sendModelOnInterface(randomSet[0], QUIZMASTER_INTERFACE, 6, 512)
player.packetDispatch.sendModelOnInterface(randomSet[1], QUIZMASTER_INTERFACE, 7, 512)
player.packetDispatch.sendModelOnInterface(randomSet[2], QUIZMASTER_INTERFACE, 8, 512)
player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 6, 512,0,0)
player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 7, 512,0,0)
player.packetDispatch.sendAngleOnInterface(QUIZMASTER_INTERFACE, 8, 512,0,0)
return correctButton
}
// Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas.
val tableRoll = WeightBasedTable.create(
WeightedItem(Items.LAMP_2528, 1, 1, 1.0, false),
WeightedItem(Items.CABBAGE_1965, 1, 1, 1.0, false),
WeightedItem(Items.DIAMOND_1601, 1, 1, 1.0, false),
WeightedItem(Items.BUCKET_1925, 1, 1, 1.0, false),
WeightedItem(Items.FLIER_956, 1, 1, 1.0, false),
WeightedItem(Items.OLD_BOOT_685, 1, 1, 1.0, false),
WeightedItem(Items.BODY_RUNE_559, 1, 1, 1.0, false),
WeightedItem(Items.ONION_1957, 1, 1, 1.0, false),
WeightedItem(Items.MITHRIL_SCIMITAR_1329, 1, 1, 1.0, false),
WeightedItem(Items.CASKET_405, 1, 1, 1.0, false),
WeightedItem(Items.STEEL_PLATEBODY_1119, 1, 1, 1.0, false),
WeightedItem(Items.NATURE_RUNE_561, 20, 20, 1.0, false),
)
}
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
0 -> npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "<col=8A0808>O D D</col> <col=8A088A>O N E</col> <col=08088A>O U T</col>").also { stage++ }
1 -> player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...").also { stage++ }
2 -> npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "<col=FF0000>${player?.username}</col>!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!").also { stage++ }
3 -> {
setAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player!!))
player!!.interfaceManager.openChatbox(QUIZMASTER_INTERFACE)
stage++
}
4-> {
if (buttonID == getAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) {
// Correct Answer
setAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1)
if (getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) {
npc(FacialExpression.FRIENDLY,"<col=08088A>CONGRATULATIONS!</col>", "You are a <col=8A0808>WINNER</col>!", "Please choose your <col=08088A>PRIZE</col>!")
stage = 5
} else {
npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!")
stage = 3
}
} else {
// Wrong Answer
npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!")
stage = 3
}
}
// Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas.
5 -> options("1000 Coins", "Random Item").also { stage++ }
6 -> {
resetAnimator(player!!)
returnPlayer(player!!)
when (buttonID) {
1 -> {
queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int ->
addItemOrDrop(player!!, Items.COINS_995, 1000)
return@queueScript stopExecuting(player!!)
}
}
2 -> {
queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int ->
addItemOrDrop(player!!, tableRoll.roll()[0].id)
return@queueScript stopExecuting(player!!)
}
}
}
removeAttributes(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER)
stage = END_DIALOGUE
end()
}
}
}
}

View file

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

View file

@ -4,17 +4,14 @@ import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import content.global.ame.RandomEventNPC
import core.api.utils.WeightBasedTable
import java.lang.Integer.max
val ids = 391..396
val ids = (391..396).toList()
class RiverTrollRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(391){
override fun talkTo(npc: NPC) {}
override fun init() {
super.init()
val index = max(0, (player.properties.combatLevel / 20) - 1)
val id = ids.toList()[index]
val id = idForCombatLevel(ids, player)
this.transform(id)
this.attack(player)
sendChat("Fishies be mine, leave dem fishies!")

View file

@ -4,17 +4,14 @@ import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import content.global.ame.RandomEventNPC
import core.api.utils.WeightBasedTable
import kotlin.math.max
val ids = 413..418
val ids = (413..418).toList()
class RockGolemRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(413){
override fun talkTo(npc: NPC) {}
override fun init() {
super.init()
val index = max(0,(player.properties.combatLevel / 20) - 1)
val id = ids.toList()[index]
val id = idForCombatLevel(ids, player)
this.transform(id)
this.attack(player)
sendChat("Raarrrgghh! Flee human!")

View file

@ -4,15 +4,13 @@ import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import content.global.ame.RandomEventNPC
import core.api.utils.WeightBasedTable
import kotlin.math.*
class ShadeRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(425){
val ids = (425..430).toList()
override fun talkTo(npc: NPC) {}
override fun init() {
super.init()
val index = max(0, min(ids.size, (player.properties.combatLevel / 20) - 1))
val id = ids[index]
val id = idForCombatLevel(ids, player)
this.transform(id)
this.attack(player)
sendChat("Leave this place!")

View file

@ -32,4 +32,8 @@ class StrangePlantBehavior() : NPCBehavior(NPCs.STRANGE_PLANT_408) {
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
}
}

View file

@ -1,4 +1,4 @@
package content.global.ame.events.supriseexam
package content.global.ame.events.surpriseexam
import core.game.component.Component
import core.game.dialogue.FacialExpression

View file

@ -1,7 +1,7 @@
package content.global.ame.events
import core.game.node.entity.player.Player
import content.global.ame.events.supriseexam.SurpriseExamUtils
import content.global.ame.events.surpriseexam.SurpriseExamUtils
import core.game.dialogue.DialogueFile
import core.game.system.timer.impl.AntiMacro

View file

@ -1,4 +1,4 @@
package content.global.ame.events.supriseexam
package content.global.ame.events.surpriseexam
import core.game.dialogue.DialogueFile
import core.tools.END_DIALOGUE

View file

@ -1,4 +1,4 @@
package content.global.ame.events.supriseexam
package content.global.ame.events.surpriseexam
import core.game.node.entity.npc.NPC
import org.rs09.consts.Components

View file

@ -1,4 +1,4 @@
package content.global.ame.events.supriseexam
package content.global.ame.events.surpriseexam
import core.game.component.Component
import core.game.node.entity.player.Player
@ -9,20 +9,22 @@ import org.rs09.consts.NPCs
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import content.global.handlers.iface.ExperienceInterface
import core.api.MapArea
import core.api.removeItem
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
class SupriseExamListeners : InteractionListener {
val MORDAUT = NPCs.MR_MORDAUT_6117
val BOOK_OF_KNOWLEDGE = Items.BOOK_OF_KNOWLEDGE_11640
class SupriseExamListeners : InteractionListener, MapArea {
override fun defineListeners() {
on(MORDAUT, IntType.NPC, "talk-to"){ player, node ->
on(NPCs.MR_MORDAUT_6117, IntType.NPC, "talk-to") { player, node ->
player.faceLocation(Location.create(1886, 5024, 0))
val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT,0) == 3
player.dialogueInterpreter.open(MordautDialogue(examComplete),node.asNpc())
val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT, 0) == 3
player.dialogueInterpreter.open(MordautDialogue(examComplete), node.asNpc())
return@on true
}
on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open"){ player, node ->
on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open") { player, node ->
val correctDoor = player.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1)
if(correctDoor == -1){
@ -39,9 +41,9 @@ class SupriseExamListeners : InteractionListener {
return@on true
}
on(BOOK_OF_KNOWLEDGE, IntType.ITEM, "read"){ player, _ ->
player.setAttribute("caller"){skill: Int,p: Player ->
if(p.inventory.remove(Item(BOOK_OF_KNOWLEDGE))) {
on(Items.BOOK_OF_KNOWLEDGE_11640, IntType.ITEM, "read") { player, _ ->
player.setAttribute("caller") { skill: Int, p: Player ->
if (removeItem(p, Items.BOOK_OF_KNOWLEDGE_11640)) {
val level = p.skills.getStaticLevel(skill)
val experience = level * 15.0
p.skills.addExperience(skill, experience)
@ -54,8 +56,16 @@ class SupriseExamListeners : InteractionListener {
}
override fun defineDestinationOverrides() {
setDest(IntType.NPC,MORDAUT){ _, _ ->
setDest(IntType.NPC, NPCs.MR_MORDAUT_6117) { _, _ ->
return@setDest Location.create(1886, 5025, 0)
}
}
}
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(ZoneBorders.forRegion(7502))
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP)
}
}

View file

@ -1,21 +1,18 @@
package content.global.ame.events.supriseexam
package content.global.ame.events.surpriseexam
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.*
import core.game.node.entity.impl.PulseType
import core.game.node.entity.player.Player
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.map.Location
import org.rs09.consts.Components
import org.rs09.consts.Items
import core.ServerConstants
import core.game.node.entity.player.link.TeleportManager
object SurpriseExamUtils {
val SE_KEY_LOC = "/save:original-loc"
val SE_KEY_INDEX = "supexam:index"
val SE_LOGOUT_KEY = "suprise_exam"
val SE_DOOR_KEY = "supexam:door"
val INTER_PATTERN_CHILDS = intArrayOf(6,7,8)
val INTER_OPTION_CHILDS = intArrayOf(10,11,12,13)
@ -29,28 +26,16 @@ object SurpriseExamUtils {
intArrayOf(Items.FLY_FISHING_ROD_309,Items.BARBARIAN_ROD_11323,Items.SMALL_FISHING_NET_303,Items.HARPOON_311)
)
fun teleport(player: Player){
player.setAttribute(SE_KEY_LOC,player.location)
registerLogoutListener(player, SE_LOGOUT_KEY){p ->
p.location = getAttribute(p, SE_KEY_LOC, ServerConstants.HOME_LOCATION)
}
player.properties.teleportLocation = Location.create(1886, 5025, 0)
fun teleport(player: Player) {
kidnapPlayer(player, Location.create(1886, 5025, 0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player){
player.properties.teleportLocation = player.getAttribute(SE_KEY_LOC,null)
clearLogoutListener(player, SE_LOGOUT_KEY)
player.removeAttribute(SE_KEY_LOC)
player.removeAttribute(SE_KEY_INDEX)
player.removeAttribute(SE_KEY_CORRECT)
returnPlayer(player)
removeAttributes(player, SE_KEY_INDEX, SE_KEY_CORRECT)
player.pulseManager.run(object : Pulse(2){
override fun pulse(): Boolean {
val reward = Item(Items.BOOK_OF_KNOWLEDGE_11640)
if(!player.inventory.add(reward)){
GroundItemManager.create(reward,player)
}
addItemOrDrop(player, Items.BOOK_OF_KNOWLEDGE_11640)
return true
}
}, PulseType.CUSTOM_1)

View file

@ -4,17 +4,14 @@ import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import content.global.ame.RandomEventNPC
import core.api.utils.WeightBasedTable
import kotlin.math.max
val ids = 438..443
val ids = (438..443).toList()
class TreeSpiritRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(438){
override fun talkTo(npc: NPC) {}
override fun init() {
super.init()
val index = max(0,(player.properties.combatLevel / 20) - 1)
val id = ids.toList()[index]
val id = idForCombatLevel(ids, player)
this.transform(id)
this.attack(player)
sendChat("Leave these woods and never return!")

View file

@ -4,15 +4,13 @@ import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import content.global.ame.RandomEventNPC
import core.api.utils.WeightBasedTable
import kotlin.math.*
class ZombieRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(419){
val ids = (419..424).toList()
override fun talkTo(npc: NPC) {}
override fun init() {
super.init()
val index = max(0, min(ids.size, (player.properties.combatLevel / 20) - 1))
val id = ids[index]
val id = idForCombatLevel(ids, player)
this.transform(id)
this.attack(player)
sendChat("Brainsssss!")

View file

@ -12,15 +12,16 @@ import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.zone.ZoneBorders
import core.game.world.update.flag.*
import core.tools.RandomFunction
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import core.ServerConstants
import core.api.log
import core.game.bots.AIRepository
import core.game.bots.CombatBotAssembler
import core.game.bots.Script
import core.game.interaction.IntType
import core.game.interaction.InteractionListeners
import core.tools.Log
import java.io.File
import java.io.FileReader
import java.time.LocalDateTime
@ -40,21 +41,27 @@ import kotlin.random.Random
* @author Ceikry
*/
//Adventure Bots v4.0.0 -Expansion Edition-
// Adventure Bots v1.1.0 : Expansion Edition (Previously v4.0.0)
// Super Grand Exchange Update
class Adventurer(val style: CombatStyle): Script() {
var city: Location = lumbridge
var ticks = 0
var freshspawn = true
var sold = false
private val geloc: Location = if (Random.nextBoolean()){
Location.create(3165, 3487, 0)
}else{
Location.create(3164, 3492, 0)
}
var poiloc: Location = karamja
var geSocialLoc: Location = getRandomGESocialLocation()
var geClerkLoc: Location = getRandomGELocation()
var geClerksloc: Location = neGEClerk
var freshspawn: Boolean = true
var sold: Boolean = false
var poi: Boolean = false
val chance: Int = if (cityLocationsGE.contains(city)) 3500 else 3000
var ticks: Int = 0
var counter: Int = 0
var random: Int = (5..30).random()
val waitTotal: Int = 8
var returnToAdventure: Int = 0
var geWait: Int = 0
var geLongWait: Int = 0
val type = when(style){
CombatStyle.MELEE -> CombatBotAssembler.Type.MELEE
@ -74,23 +81,81 @@ class Adventurer(val style: CombatStyle): Script() {
}
override fun toString(): String {
return "${bot.name} is an Adventurer bot at ${bot.location}! State: $state - City: $city"
return "${bot.username} is an Adventurer bot " +
"at ${bot.location}! " +
"State: $state - " +
"City: $city - " +
"Ticks: $ticks - " +
"Freshspawn: $freshspawn - " +
"Sold: $sold - " +
"Counter: $counter"
}
var state = State.START
fun getRandomCity(): Location{
private fun getRandomCity(): Location{
return cities.random()
}
fun getRandomPoi(): Location{
private fun getRandomPoi(): Location{
return pois.random()
}
private fun getRandomGESocialLocation(): Location{
return socialLocationsGE.random()
}
private fun getRandomGELocation(): Location {
return cityLocationsGE.random()
}
private fun randomNumberFromOne(maxInt: Int): Int {
return Random.nextInt(0, maxInt)
}
private fun otherPlayersNearby(): Boolean {
val localPlayers = RegionManager.getLocalPlayers(bot)
val otherPlayers = localPlayers.filter { it.name != bot.name }
return otherPlayers.isNotEmpty()
}
private fun checkNearBank() {
if(bankMap[city] == null){
scriptAPI.teleport(getRandomCity().also { city = it })
} else {
if(bankMap[city]?.insideBorder(bot) == true){
state = State.FIND_BANK
} else {
bankMap[city]?.let { scriptAPI.walkTo(it.randomLoc) }
}
}
}
private fun checkCounter(maxCounter: Int) {
if (counter++ >= maxCounter) {
state = State.TELEPORTING
}
}
private fun teleportToRandomCity() {
city = getRandomCity()
when (city) {
neGEClerk -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-3,2,0,1,0)) }
swGEClerk -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-2,3,-1,0,0)) }
nwGEBanker -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-2,0,-3,2,0)) }
seGEBanker -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,0,2,-2,3,0)) }
else -> { scriptAPI.teleport(scriptAPI.randomizeLocationInRanges(city,-1,1,-1,1,0)) }
}
}
val resources = listOf(
"Rocks","Tree","Oak","Willow",
"Maple tree","Yew","Magic tree",
"Teak","Mahogany")
//TODO: Optimise and adjust how bots handle picking up ground items further.
fun immerse() {
if (counter++ == 180) {state = State.TELEPORTING
}
if (counter++ >= Random.nextInt(150,300)) { state = State.TELEPORTING }
val items = AIRepository.groundItems[bot]
if (Random.nextBoolean()) {
if (items.isNullOrEmpty()) {
@ -98,33 +163,13 @@ class Adventurer(val style: CombatStyle): Script() {
state = State.LOOT_DELAY
}
if (bot.inventory.isFull) {
if(bankMap[city] == null){
scriptAPI.teleport(getRandomCity().also { city = it })
} else {
if(bankMap[city]?.insideBorder(bot) == true){
state = State.FIND_BANK
} else {
scriptAPI.walkTo(bankMap[city]?.randomLoc ?: Location(0, 0, 0))
}
}
checkNearBank()
}
} else {
if (bot.inventory.isFull){
if(bankMap[city] == null){
scriptAPI.teleport(getRandomCity().also { city = it })
} else {
if(bankMap[city]?.insideBorder(bot) == true){
state = State.FIND_BANK
} else {
scriptAPI.walkTo(bankMap[city]?.randomLoc ?: Location(0, 0, 0))
}
}
checkNearBank()
} else {
val resources = listOf(
"Rocks","Tree","Oak","Willow",
"Maple tree","Yew","Magic tree",
"Teak","Mahogany")
val resource = scriptAPI.getNearestNodeFromList(resources,true)
if(resource != null){
if(resource.name.contains("ocks")) InteractionListeners.run(resource.id,
@ -137,18 +182,17 @@ class Adventurer(val style: CombatStyle): Script() {
}
fun refresh() {
// log(this::class.java, Log.WARN, "${bot.username} refreshed from $state at $city with $ticks and $counter counter.")
scriptAPI.teleport(lumbridge)
freshspawn = true
state = State.START
}
var poi = false
var poiloc = karamja
//Adventure Bots Actual Code STARTS HERE!!!
// 100 ticks = 60 seconds
override fun tick() {
ticks++
if (ticks++ >= 800) {
// Hard refresh
if (ticks >= 1000) {
ticks = 0
refresh()
return
@ -186,49 +230,42 @@ class Adventurer(val style: CombatStyle): Script() {
}
return
} else {
state = State.EXPLORE
state = State.ADVENTURE
}
}
State.START -> {
if (freshspawn) {
freshspawn = false
scriptAPI.randomWalkTo(lumbridge, 20)
scriptAPI.randomWalkTo(lumbridge, randomNumberFromOne(25))
} else {
city = getRandomCity()
state = State.TELEPORTING
}
}
State.TELEPORTING -> {
if (freshspawn){ freshspawn = false }
teleportToRandomCity()
poi = false
sold = false
ticks = 0
counter = 0
if (bot.location != city) {
poi = false
scriptAPI.teleport(city)
} else {
poi = false
state = State.EXPLORE
}
state = State.ADVENTURE
return
}
State.EXPLORE -> {
if (counter++ == 350) {
state = State.TELEPORTING
}
val chance = if (city == ge || city == ge2) 5000 else 2500
if (RandomFunction.random(chance) <= 10) {
val nearbyPlayers = RegionManager.getLocalPlayers(bot)
if (nearbyPlayers.isNotEmpty()) {
State.ADVENTURE -> {
checkCounter(800)
if (randomNumberFromOne(chance) <= 10) {
if (otherPlayersNearby()) {
ticks = 0
dialogue()
}
}
if (RandomFunction.random(1000) <= 150 && !poi) {
val roamDistance = if (city != ge && city != ge2) 225 else 7
if ((city == ge || city == ge2) && RandomFunction.random(100) < 90) {
if (!poi && randomNumberFromOne(1000) <= 75) {
val roamDistance = if (!cityLocationsGE.contains(city)) 225 else randomNumberFromOne(5)
if (cityLocationsGE.contains(city) && randomNumberFromOne(100) < 90) {
if (!bot.bank.isEmpty) {
state = State.FIND_GE
}
@ -238,33 +275,46 @@ class Adventurer(val style: CombatStyle): Script() {
return
}
if (RandomFunction.random(1000) <= 50 && poi){
if (poi && randomNumberFromOne(1000) <= 100){
immerse()
return
}
if (poi && randomNumberFromOne(1000) <= 25){
dialogue()
}
if (poi && randomNumberFromOne(1000) <= 50){
val roamDistancePoi = when(poiloc){
teakfarm, crawlinghands -> 5
treegnome -> 50
isafdar -> 40
eaglespeek -> 40
keldagrimout -> 30
teak1 -> 30
miningguild -> 5
magics, coal -> 7
gemrocks, chaosnpc, chaosnpc2 -> 1
magics, coalTrucks -> 7
miningguild, teakfarm, crawlinghands -> 5
varLumberYard -> 20
keldagrimout, teak1 -> 30
eaglespeek, isafdar -> 40
treegnome -> 50
else -> 60
}
scriptAPI.randomWalkTo(poiloc,roamDistancePoi)
return
}
if (RandomFunction.random(1000) <= 75) {
if (city != ge && city != ge2) {
if (randomNumberFromOne(1000) <= 75) {
if (!cityLocationsGE.contains(city)) {
ticks = 0
immerse()
return
} else {
return
} else if (randomNumberFromOne(chance) <= 55 && otherPlayersNearby()) {
ticks = 0
dialogue()
}
}
if (RandomFunction.random(20000) <= 60 && !poi) {
if (cityLocationsGE.contains(city) && randomNumberFromOne(1000) <= 50) {
state = State.IDLE_GE
}
if (!poi && randomNumberFromOne(1000) <= 5) {
poiloc = getRandomPoi()
city = teak1
poi = true
@ -272,26 +322,24 @@ class Adventurer(val style: CombatStyle): Script() {
return
}
if ((city == ge || city == ge2) && RandomFunction.random(1000) >= 999) {
ticks = 0
city = getRandomCity()
if (cityLocationsGE.contains(city) && randomNumberFromOne(1000) <= 100) {
state = State.TELEPORTING
}
if (city == ge || city == ge2) {
return
}
if (city == teak1 && counter++ >= 240){
city = getRandomCity()
state = State.TELEPORTING
if (cityLocationsGE.contains(city)) {
return
}
if (counter++ >= 240 && RandomFunction.random(100) >= 10) {
if (poi && randomNumberFromOne(1000) <= 20){
state = State.TELEPORTING
return
}
if (counter++ >= 750 && randomNumberFromOne(100) <= 50) {
// log(this::class.java, Log.FINE, "${bot.username} has moved on to a different city at $ticks ticks and $counter counter.")
city = getRandomCity()
if (RandomFunction.random(100) % 2 == 0) {
counter = 0
ticks = 0
if (randomNumberFromOne(100) % 2 == 0) {
state = State.TELEPORTING
} else {
if (citygroupA.contains(city)) {
@ -309,122 +357,122 @@ class Adventurer(val style: CombatStyle): Script() {
return
}
State.GE -> {
var ge = false
if (counter++ == 180) {
state = State.TELEPORTING
}
if (!sold) {
if (counter++ >= 15) {
sold = true
ge = true
State.IDLE_GE -> {
returnToAdventure = Random.nextInt(350, 750)
if (counter++ >= returnToAdventure){
if (randomNumberFromOne(100) <= 25){
ticks = 0
counter = 0
poiloc = getRandomPoi()
city = teak1
poi = true
scriptAPI.teleport(poiloc)
state = State.ADVENTURE
return
} else {
counter = 0
ticks = 0
scriptAPI.sellAllOnGeAdv()
state = State.TELEPORTING
return
}
}
if (cityLocationsGE.contains(city)){
if (randomNumberFromOne(1000) <= 5) {
ticks = 0
geSocialLoc = scriptAPI.randomizeLocationInRanges(getRandomGESocialLocation(),-1,1,-1,1,0)
} else if (randomNumberFromOne(1000) <= 10) {
ticks = 0
scriptAPI.randomWalkTo(geSocialLoc, randomNumberFromOne(5))
return
}
if (randomNumberFromOne(1000) <= 5 && otherPlayersNearby()){
ticks = 0
dialogue()
} else if (randomNumberFromOne(1000) <= 250){
return
}
} else if (ge && sold) {
ge = false
city = getRandomCity()
state = State.TELEPORTING
}
return
}
State.FIND_GE -> {
if (counter++ == 180) {
state = State.TELEPORTING
}
sold = false
val ge: Scenery? = scriptAPI.getNearestNode("Desk", true) as Scenery?
if (ge == null || bot.bank.isEmpty) state = State.EXPLORE
if (ge == null || bot.bank.isEmpty) state = State.ADVENTURE
class GEPulse : MovementPulse(bot, ge, DestinationFlag.OBJECT) {
override fun pulse(): Boolean {
bot.faceLocation(ge?.location)
state = State.GE
return true
return true.also { state = State.GE }
}
}
if (ge == null || bot.bank.isEmpty) state = State.ADVENTURE
if (ge != null && !bot.bank.isEmpty) {
counter = 0
scriptAPI.randomWalkTo(geloc, 3)
GameWorld.Pulser.submit(GEPulse())
if (randomNumberFromOne(1000) <= 25 && otherPlayersNearby()){
dialogue()
scriptAPI.randomWalkTo(geSocialLoc, randomNumberFromOne(5))
} else if (randomNumberFromOne(500) <= 50) {
GameWorld.Pulser.submit(GEPulse())
}
}
checkCounter(500)
return
}
State.GE -> {
geClerksloc = clerkLocationsGe.random()
geWait = Random.nextInt(35, 100)
geLongWait = Random.nextInt(350, 750)
if (!sold) {
if (randomNumberFromOne(500) <= 25){ scriptAPI.randomWalkTo(geClerksloc, randomNumberFromOne(4))}
if (counter++ >= geWait) {
scriptAPI.randomWalkTo(geClerksloc, randomNumberFromOne(1))
sold = true
counter = 0
ticks = 0
scriptAPI.sellAllOnGeAdv()
state = State.TELEPORTING
return
}
} else if (counter++ >= geLongWait) {
state = State.TELEPORTING
return
}
checkCounter(1000)
return
}
State.FIND_BANK -> {
if (counter++ == 300) {
state = State.TELEPORTING
}
val bank: Scenery? = scriptAPI.getNearestNode("Bank booth", true) as Scenery?
if (badedge.insideBorder(bot) || bot.location == badedge2 || bot.location == badedge3 || bot.location == badedge4) {
bot.randomWalk(5, 5)
}
if (bank == null) state = State.EXPLORE
class BankingPulse : MovementPulse(bot, bank, DestinationFlag.OBJECT) {
override fun pulse(): Boolean {
bot.faceLocation(bank?.location)
state = State.IDLE_BANKS
return true
}
}
if (bank != null) {
bot.pulseManager.run(BankingPulse())
if (bank == null) { state = State.TELEPORTING }
if (bank != null && randomNumberFromOne(100) <= 5) {
scriptAPI.depositAtBank()
} else if (bank != null && randomNumberFromOne(100) <= 5){
scriptAPI.randomWalkTo(bank.location,3)
}
checkCounter(500)
return
}
State.IDLE_BANKS -> {
if (counter++ == 300) {
state = State.TELEPORTING
}
if (RandomFunction.random(1000) < 100) {
for (item in bot.inventory.toArray()) {
item ?: continue
when (item.id) {
1359, 590, 1271, 995 -> continue
}
bot.bank.add(item)
bot.inventory.remove(item)
}
counter = 0
state = State.EXPLORE
}
return
}
State.FIND_CITY -> {
if (counter++ >= 600 || (city == ge || city == ge2)) {
counter = 0
if (counter++ >= 500 || cityLocationsGE.contains(city)){
scriptAPI.teleport(getRandomCity().also { city = it })
state = State.EXPLORE
state = State.ADVENTURE
}
if (bot.location.equals(city)) {
state = State.EXPLORE
state = State.ADVENTURE
} else {
scriptAPI.randomWalkTo(city, 5)
scriptAPI.randomWalkTo(city, randomNumberFromOne(10))
}
checkCounter(600)
return
}
State.IDLE_CITY -> {
if (counter++ == 300) {
state = State.TELEPORTING
}
var random = (120..300).random()
if (counter++ == random && RandomFunction.random(1000) % 33 == 0) {
counter = 0
state = State.EXPLORE
}
return
}
}
}
fun dialogue() {
val localPlayer = RegionManager.getLocalPlayers(bot).random()
val until = 1225 - dateCode
val lineStd = dialogue.getLines("standard").rand()
var lineAlt = ""
@ -455,20 +503,29 @@ class Adventurer(val style: CombatStyle): Script() {
dateCode == 404 -> lineAlt = dialogue.getLines("easter").rand()
}
val chat = if (lineAlt.isNotEmpty() && Random.nextBoolean()) { lineAlt } else { lineStd }
.replace("@name", localPlayer.username)
.replace("@timer", until.toString())
scriptAPI.sendChat(chat)
var localPlayers = RegionManager.getLocalPlayers(bot)
if (localPlayers.isNotEmpty()) {
val localPlayer = localPlayers
.filter { it.name != bot.name }
.randomOrNull()
if (localPlayer != null) {
val chat = if (lineAlt.isNotEmpty() && Random.nextBoolean()) { lineAlt } else { lineStd }
.replace("@name", localPlayer.username)
.replace("@timer", until.toString())
scriptAPI.sendChat(chat)
} else {
val chat = if (lineAlt.isNotEmpty() && Random.nextBoolean()) { lineAlt } else { lineStd }
scriptAPI.sendChat(chat)
}
}
}
enum class State{
START,
EXPLORE,
ADVENTURE,
FIND_BANK,
IDLE_BANKS,
FIND_CITY,
IDLE_CITY,
IDLE_GE,
GE,
TELEPORTING,
LOOT,
@ -489,44 +546,94 @@ class Adventurer(val style: CombatStyle): Script() {
}
companion object {
val badedge = ZoneBorders(3094, 3494, 3096, 3497)
val badedge2 = Location.create(3094, 3492, 0)
val badedge3 = Location.create(3094, 3490, 0)
val badedge4 = Location.create(3094, 3494, 0)
// Start Cities
val yanille: Location = Location.create(2615, 3104, 0)
val ardougne: Location = Location.create(2662, 3304, 0)
val seers: Location = Location.create(2726, 3485, 0)
val edgeville: Location = Location.create(3088, 3486, 0)
val ge: Location = Location.create(3168, 3487, 0)
val ge2: Location = Location.create(3161, 3493, 0)
val catherby: Location = Location.create(2809, 3435, 0)
val falador: Location = Location.create(2965, 3380, 0)
val varrock: Location = Location.create(3213, 3428, 0)
val draynor: Location = Location.create(3080, 3250, 0)
val rimmington: Location = Location.create(2977, 3239, 0)
val lumbridge: Location = Location.create(3222, 3219, 0)
val karamja = Location.create(2849, 3033, 0)
val alkharid = Location.create(3297, 3219, 0)
val feldiphills = Location.create(2535, 2919, 0)
val isafdar = Location.create(2241, 3217, 0)
val eaglespeek = Location.create(2333, 3579, 0)
val canafis = Location.create(3492, 3485, 0)
val treegnome = Location.create(2437, 3441, 0)
val teak1 = Location.create(2334, 3048, 0)
val teakfarm = Location.create(2825, 3085, 0)
val keldagrimout = Location.create(2724,3692,0)
val miningguild = Location.create(3046,9740,0)
val magics = Location.create(2285,3146,0)
val coal = Location.create(2581,3481,0)
val crawlinghands = Location.create(3422,3548,0)
val gemrocks = Location.create(2825,2997,0)
val chaosnpc = Location.create(2612,9484,0)
val chaosnpc2 = Location.create(2580,9501,0)
val taverly = Location.create(2909, 3436, 0)
val karamja: Location = Location.create(2849, 3033, 0)
val alkharid: Location = Location.create(3297, 3219, 0)
// Start POI
val feldiphills: Location = Location.create(2535, 2919, 0)
val isafdar: Location = Location.create(2241, 3217, 0)
val eaglespeek: Location = Location.create(2333, 3579, 0)
val canafis: Location = Location.create(3492, 3485, 0)
val treegnome: Location = Location.create(2437, 3441, 0)
val teak1: Location = Location.create(2334, 3048, 0)
val teakfarm: Location = Location.create(2825, 3085, 0)
val keldagrimout: Location = Location.create(2724,3692, 0)
val miningguild: Location = Location.create(3046,9740, 0)
val magics: Location = Location.create(2285,3146, 0)
val coalTrucks: Location = Location.create(2581,3481, 0)
val crawlinghands: Location = Location.create(3422,3548, 0)
val gemrocks: Location = Location.create(2825,2997, 0)
val chaosnpc: Location = Location.create(2612,9484, 0)
val chaosnpc2: Location = Location.create(2586, 9501, 0)
val varLumberYard: Location = Location.create(3289, 3482, 0)
val taverly: Location = Location.create(2909, 3436, 0)
val swGEClerk: Location = Location.create(3164, 3487, 0)
val neGEClerk: Location = Location.create(3165, 3492, 0)
val nwGEBanker: Location = Location.create(3162, 3490, 0)
val seGEBanker: Location = Location.create(3167, 3489, 0)
val badedge = ZoneBorders(3094, 3494, 3096, 3497)
val badedge2: Location = Location.create(3094, 3492, 0)
val badedge3: Location = Location.create(3094, 3490, 0)
val badedge4: Location = Location.create(3094, 3494, 0)
var citygroupA = listOf(falador, varrock, draynor, rimmington, lumbridge, edgeville)
var citygroupB = listOf(yanille, ardougne, seers, catherby)
val cities = listOf(
swGEClerk, neGEClerk, nwGEBanker, seGEBanker,
yanille, ardougne, seers, catherby,
falador, varrock, draynor, rimmington,
lumbridge, edgeville
)
val pois = listOf(
karamja, karamja, alkharid,
alkharid, feldiphills, feldiphills,
isafdar, eaglespeek, eaglespeek,
canafis, treegnome, treegnome,
teak1, teakfarm, keldagrimout,
miningguild, coalTrucks, crawlinghands,
magics, gemrocks, chaosnpc, chaosnpc,
chaosnpc2, taverly,
varLumberYard)
val cityLocationsGE = listOf(swGEClerk, neGEClerk, nwGEBanker, seGEBanker)
val socialLocationsGE = listOf(
Location.create(3158, 3483, 0),
Location.create(3165, 3480, 0),
Location.create(3172, 3483, 0),
Location.create(3174, 3489, 0),
Location.create(3171, 3497, 0),
Location.create(3164, 3499, 0),
Location.create(3157, 3497, 0),
Location.create(3155, 3489, 0),
Location.create(3167, 3492, 0),
Location.create(3162, 3492, 0),
Location.create(3162, 3487, 0),
Location.create(3167, 3487, 0)
)
val clerkLocationsGe = listOf(
Location.create(3165, 3492, 0),
Location.create(3164, 3492, 0),
Location.create(3164, 3487, 0),
Location.create(3165, 3487, 0)
)
var bankMap = mapOf<Location, ZoneBorders>(
falador to ZoneBorders(2950, 3374, 2943, 3368),
varrock to ZoneBorders(3182, 3435, 3189, 3446),
@ -538,22 +645,10 @@ class Adventurer(val style: CombatStyle): Script() {
catherby to ZoneBorders(2807, 3438, 2811, 3441)
)
val cities = listOf(yanille, ardougne, seers, catherby, falador, varrock,
draynor, rimmington, lumbridge, ge, ge2, edgeville)
val pois = listOf(
karamja, karamja, alkharid,
alkharid, feldiphills, feldiphills,
isafdar, eaglespeek, eaglespeek,
canafis, treegnome, treegnome,
teak1, teakfarm, keldagrimout,
miningguild, coal, crawlinghands,
magics, gemrocks, chaosnpc, chaosnpc,
chaosnpc2, taverly)
private val whiteWolfMountainTop = Location(2850, 3496, 0)
private val catherbyToTopOfWhiteWolf = arrayOf(Location(2856, 3442, 0), Location(2848, 3455, 0), Location(2848, 3471, 0), Location(2848, 3487, 0))
private val tavleryToTopOfWhiteWolf = arrayOf(Location(2872, 3425, 0), Location(2863, 3440, 0), Location(2863, 3459, 0), Location(2854, 3475, 0), Location(2859, 3488, 0))
val common_stuck_locations = mapOf(
// South of Tavlery dungeon
ZoneBorders(2878, 3386, 2884, 3395) to { it: Adventurer ->

View file

@ -7,8 +7,6 @@ import core.api.*
import core.game.bots.*
import core.game.ge.GrandExchange
import core.game.interaction.DestinationFlag
import core.game.interaction.IntType
import core.game.interaction.InteractionListeners
import core.game.interaction.MovementPulse
import core.game.node.Node
import core.game.node.entity.skill.Skills
@ -16,6 +14,7 @@ import core.game.node.item.Item
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import org.rs09.consts.Items
import content.data.Quests
@PlayerCompatible
@ScriptName("Falador Cannonball Smelter")
@ -273,6 +272,6 @@ class CannonballSmelter : Script() {
skills.put(Skills.HITPOINTS,99)
skills.put(Skills.DEFENCE,99)
skills.put(Skills.SMITHING,35)
quests.add("Dwarf Cannon")
quests.add(Quests.DWARF_CANNON)
}
}

View file

@ -45,18 +45,7 @@ class BankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin
2 -> showTopics(
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to access my bank account, please.", 10),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to switch to my ${getBankAccountName(player, true)} bank account.",
13,
hasActivatedSecondaryBankAccount(player)
),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to open a secondary bank account.",
20,
!hasActivatedSecondaryBankAccount(player)
),
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to switch my default bank account.", 7),
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to check my PIN settings.", 11),
Topic(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to collect items.", 12),
Topic(core.game.dialogue.FacialExpression.ASKING, "What is this place?", 3),
@ -78,6 +67,53 @@ class BankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin
"Leave your valuables with us if you want to keep them safe."
).also { stage = END_DIALOGUE }
7 -> showTopics(
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to turn on the Clan bank, please.",
8,
!(player.getAttribute("clanbank:enabled",false))
),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to turn off the Clan bank, please.",
9,
player.getAttribute("clanbank:enabled",false)
),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to switch to my ${getBankAccountName(player, true)} bank account.",
13,
hasActivatedSecondaryBankAccount(player)
),
IfTopic(
core.game.dialogue.FacialExpression.FRIENDLY,
"I'd like to open a secondary bank account.",
20,
!hasActivatedSecondaryBankAccount(player)
),
)
8 -> {
player.setAttribute("/save:clanbank:enabled", true)
npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"The Clan bank has been enabled. " +
"If the Clan owner is online, you will access their primary bank account instead of your own."
).also { stage = 2 }
}
9 -> {
player.removeAttribute("clanbank:enabled")
npcl(
core.game.dialogue.FacialExpression.FRIENDLY,
"The Clan bank has been disabled. " +
"You will now always open your personal bank account."
).also { stage = 2 }
}
10 -> {
openBankAccount(player)
end()

View file

@ -3,160 +3,227 @@ package content.global.dialogue
import content.global.skill.farming.FarmerPayOptionDialogue
import content.global.skill.farming.Farmers
import content.global.skill.farming.FarmingPatch
import core.game.node.entity.npc.NPC
import content.global.skill.farming.PatchType
import core.api.*
import core.game.dialogue.FacialExpression
import core.game.dialogue.IfTopic
import core.game.dialogue.Topic
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import org.rs09.consts.Items
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
@Initializable
class GardenerDialoguePlugin(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) {
override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin {
return GardenerDialoguePlugin(player)
}
override fun open(vararg args: Any?): Boolean {
npc = args[0] as NPC
options("Would you look after my crops for me?","Can you sell me something?")
return true
}
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
when(stage){
0 -> when(buttonId){
1 -> player("Would you look after my crops for me?").also { stage = 10 }
2 -> player("Can you sell me something?").also { stage = 30 }
val patches = Farmers.forId(npc.id)!!.patches
when (stage) {
// TODO: Can fruit trees be chopped down by the gardener too?
START_DIALOGUE -> {
val patch = patches[0].getPatchFor(player)
showTopics(
IfTopic(
FacialExpression.ASKING,
"Would you chop my tree down for me?",
1000,
patch.patch.type == PatchType.TREE_PATCH && patch.plantable != null && patch.isGrown()
),
IfTopic(
FacialExpression.ASKING,
"Would you look after my crops for me?",
10,
!(patch.patch.type == PatchType.TREE_PATCH && patch.plantable != null && patch.isGrown())
),
Topic(FacialExpression.ASKING, "Can you give me any farming advice?", 2000),
Topic(FacialExpression.ASKING, "Can you sell me something?", 30),
Topic(FacialExpression.NEUTRAL, "That's all, thanks.", END_DIALOGUE)
)
}
10 -> npc("I might. Which one were you thinking of?").also { stage++ }
11 -> when(npc.id){
Farmers.ELSTAN.id, Farmers.LYRA.id -> options("The north-western allotment.","The south-eastern allotment.").also { stage = 15 }
Farmers.DANTAERA.id, Farmers.KRAGEN.id -> options("The north allotment.","The south allotment.").also { stage = 15 }
else -> player("Uh, that one.").also { stage++ }
10 -> {
if (patches.size > 1) {
npc("I might. Which one were you thinking of?").also { stage = 20 }
} else {
openPayGardenerDialogue(player, patches[0])
}
}
12 -> npc("Oh, right. My bad.").also { stage++ }
13 -> checkPatch(player,Farmers.forId(npc.id)!!.patches[0])
15 -> when(buttonId){
1 -> checkPatch(player,Farmers.forId(npc.id)!!.patches[0])
2 -> checkPatch(player,Farmers.forId(npc.id)!!.patches[1])
20 -> when (npc.id) {
Farmers.ELSTAN.id, Farmers.LYRA.id -> showTopics(
Topic(FacialExpression.NEUTRAL, "The north-western allotment.", 21),
Topic(FacialExpression.NEUTRAL, "The south-eastern allotment.", 22)
)
Farmers.DANTAERA.id, Farmers.KRAGEN.id -> showTopics(
Topic(FacialExpression.NEUTRAL, "The northern allotment.", 21),
Topic(FacialExpression.NEUTRAL, "The southern allotment.", 22)
)
}
21 -> openPayGardenerDialogue(player, patches[0])
22 -> openPayGardenerDialogue(player, patches[1])
30 -> npc("That depends on whether I have it to sell.","What is it that you're looking for?").also { stage++ }
31 -> options("Some plant cure.","A bucket of compost.","A rake.","(See more items)").also { stage = 32 }
32 -> when(buttonId){
1 -> player("Some plant cure.").also { stage = 100 }
2 -> player("A bucket of compost.").also { stage = 200 }
3 -> player("A rake.").also { stage = 300 }
4 -> options("A watering can.","A gardening trowel.","A seed dibber.","(See previous items)").also { stage++ }
}
33 -> when(buttonId){
1 -> player("A watering can.").also { stage = 400 }
2 -> player("A gardening trowel.").also { stage = 500 }
3 -> player("A seed dibber.").also { stage = 600 }
4 -> options("Some plant cure.","A bucket of compost.","A rake.","(See more items)").also { stage = 32 }
}
30 -> npc(FacialExpression.NEUTRAL, "That depends on whether I have it to sell. What is it", "that you're looking for?").also { stage++ }
31 -> showTopics(
Topic(FacialExpression.NEUTRAL, "Some plant cure.", 100),
Topic(FacialExpression.NEUTRAL, "A bucket of compost.", 200),
Topic(FacialExpression.NEUTRAL, "A rake.", 300),
Topic("(See more items)", 32, true)
)
32 -> showTopics(
Topic(FacialExpression.NEUTRAL, "A watering can.", 400),
Topic(FacialExpression.NEUTRAL, "A gardening trowel.", 500),
Topic(FacialExpression.NEUTRAL, "A seed dibber.", 600),
Topic("(See previous items)", 31, true),
Topic(FacialExpression.NEUTRAL, "Forget it.", 40, true)
)
100 -> npc("Plant cure, eh? I might have some put aside for myself.","Tell you what, I'll sell you some plant cure","for 25 gp if you like.").also { stage++ }
101 -> options("Yes, that sounds like a fair price.","No thanks, I can get that much cheaper.").also { stage++ }
102 -> when(buttonId){
40 -> player("Forget it, you don't have anything I need.").also { stage = END_DIALOGUE }
100 -> npc("Plant cure, eh? I might have some put aside for myself.", "Tell you what. I'll sell you some plant cure for 25 gp if", "you like.").also { stage++ }
101 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ }
102 -> when (buttonId) {
1 -> {
player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if(player.inventory.remove(Item(995,25))){
player.inventory.add(Item(Items.PLANT_CURE_6036))
player(FacialExpression.HAPPY, "Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if (removeItem(player, Item(Items.COINS_995, 25))) {
addItemOrDrop(player, Items.PLANT_CURE_6036)
} else {
player.sendMessage("You need 25 gp to pay for that.")
sendMessage(player, "You need 25 gp to pay for that.")
}
}
2 -> end()
2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE }
}
200 -> npc("A bucket of compost, eh? I might have one spare...","tell you what, I'll sell it to you for 35 gp if you like.").also { stage++ }
201 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ }
202 -> when(buttonId){
200 -> npc("A bucket of compost, eh? I might have one spare...", "tell you what, I'll sell it to you for 35 gp if you like.").also { stage++ }
201 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ }
202 -> when (buttonId) {
1 -> {
player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if(player.inventory.remove(Item(995,35))){
player.inventory.add(Item(Items.COMPOST_6032))
if (removeItem(player, Item(Items.COINS_995, 35))) {
addItemOrDrop(player, Items.COMPOST_6032)
} else {
player.sendMessage("You need 35 gp to pay for that.")
sendMessage(player, "You need 35 gp to pay for that.")
}
}
2 -> end()
2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE }
}
300 -> npc("A rake, eh? I might have one spare...","tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ }
301 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ }
302 -> when(buttonId){
300 -> npc("A rake, eh? I might have one spare...", "tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ }
301 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ }
302 -> when (buttonId) {
1 -> {
player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if(player.inventory.remove(Item(995,15))){
player.inventory.add(Item(Items.RAKE_5341))
if (removeItem(player, Item(Items.COINS_995, 15))) {
addItemOrDrop(player, Items.RAKE_5341)
} else {
player.sendMessage("You need 15 gp to pay for that.")
sendMessage(player, "You need 15 gp to pay for that.")
}
}
2 -> end()
2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE }
}
400 -> npc("A watering can, eh? I might have one spare...","tell you what, I'll sell it to you for 25 gp if you like.").also { stage++ }
401 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ }
400 -> npc("A watering can, eh? I might have one spare...", "tell you what, I'll sell it to you for 25 gp if you like.").also { stage++ }
401 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ }
402 -> when(buttonId){
1 -> {
player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if(player.inventory.remove(Item(995,25))){
player.inventory.add(Item(Items.WATERING_CAN8_5340))
if (removeItem(player, Item(Items.COINS_995, 25))) {
addItemOrDrop(player, Items.WATERING_CAN8_5340)
} else {
player.sendMessage("You need 25 gp to pay for that.")
sendMessage(player, "You need 25 gp to pay for that.")
}
}
2 -> end()
2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE }
}
500 -> npc("A gardening trowel, eh? I might have one spare...","tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ }
501 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ }
502 -> when(buttonId){
500 -> npc("A gardening trowel, eh? I might have one spare...", "tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ }
501 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ }
502 -> when (buttonId) {
1 -> {
player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if(player.inventory.remove(Item(995,15))){
player.inventory.add(Item(Items.GARDENING_TROWEL_5325))
if (removeItem(player, Item(Items.COINS_995, 15))) {
addItemOrDrop(player, Items.GARDENING_TROWEL_5325)
} else {
player.sendMessage("You need 15 gp to pay for that.")
sendMessage(player, "You need 15 gp to pay for that.")
}
}
2 -> end()
2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE }
}
600 -> npc("A seed dibber, eh? I might have one spare...","tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ }
601 -> options("Yes, that sounds fair.","No thanks, I can get that cheaper.").also { stage++ }
602 -> when(buttonId){
600 -> npc("A seed dibber, eh? I might have one spare...", "tell you what, I'll sell it to you for 15 gp if you like.").also { stage++ }
601 -> options("Yes, that sounds like a fair price.", "No thanks, I can get that much cheaper elsewhere.").also { stage++ }
602 -> when (buttonId) {
1 -> {
player("Yes, that sounds like a fair price.").also { stage = END_DIALOGUE }
if(player.inventory.remove(Item(995,15))){
player.inventory.add(Item(Items.SEED_DIBBER_5343))
if (removeItem(player, Item(Items.COINS_995, 15))) {
addItemOrDrop(player, Items.SEED_DIBBER_5343)
} else {
player.sendMessage("You need 15 gp to pay for that.")
sendMessage(player, "You need 15 gp to pay for that.")
}
}
2 -> end()
2 -> player("No thanks, I can get that much cheaper elsewhere.").also { stage = END_DIALOGUE }
}
// Note: This dialogue changes slightly in April 2009, and significantly in December 2009
1000 -> npc(FacialExpression.THINKING, "Why? You look like you could chop it down yourself!").also { stage++ }
1001 -> showTopics(
Topic(FacialExpression.NEUTRAL, "Yes, you're right - I'll do it myself.", END_DIALOGUE),
Topic(FacialExpression.NEUTRAL, "I can't be bothered - I'd rather pay you to do it.", 1020)
)
1020 -> npc(FacialExpression.NEUTRAL, "Well, it's a lot of hard work - if you pay me 200 GP", "I'll chop it down for you.").also { stage++ }
1021 -> {
if (inInventory(player, Items.COINS_995, 200)) {
showTopics(
Topic(FacialExpression.NEUTRAL, "Here's 200GP - chop my tree down please.", 1022),
Topic(FacialExpression.NEUTRAL, "I don't want to pay that much, sorry.", END_DIALOGUE)
)
} else {
player("I don't have that much money on me.").also { stage = END_DIALOGUE } // not authentic
}
}
1022 -> {
end()
if (removeItem(player, Item(Items.COINS_995, 200))) {
patches[0].getPatchFor(player).clear()
}
}
2000 -> {
val advice = arrayOf(
"There are four main Farming areas - Elstan looks after an area south of Falador, Dantaera has one to the north of Catherby, Kragen has one near Ardougne, and Lyra looks after a place in north Morytania.",
"If you want to grow fruit trees you could try a few places: Catherby and Brimhaven have a couple of fruit tree patches, and I hear that the gnomes are big on that sort of thing.",
"Bittercap mushrooms can only be grown in a special patch in Morytania, near the Mort Myre swamp. There the ground is especially dank and suited to growing poisonous fungi.",
"There is a special patch for growing Belladonna - I believe it's somewhere near Draynor Manor, where the ground is a tad 'unblessed'.",
"Don't just throw away your weeds after you've raked a patch - put them in a compost bin and make some compost.",
"Applying compost to a patch will not only reduce the chance that your crops will get diseased, but you will also grow more crops to harvest.",
"Supercompost is far better than normal compost, but more expensive to make. You need to rot the right type of item; show me an item, and I'll tell you if it's super-compostable or not.",
"Tree seeds must be grown in a plantpot of soil into a sapling, and then transferred to a tree patch to continue growing to adulthood.",
"You don't have to buy all your plantpots you know, you can make them yourself on a pottery wheel. If you're a good enough ${if (player!!.isMale) "craftsman" else "craftswoman"}, that is.",
"You can fill plantpots with soil from Farming patches, if you have a gardening trowel.",
"Vegetables, hops and flowers are far more likely to grow healthily if you water them periodically.",
"The only way to cure a bush or tree of disease is to prune away the diseased leaves with a pair of secateurs. For all other crops I would just apply some plant-cure.",
"If you need to be rid of your fruit trees for any reason, all you have to do is chop them down and then dig up the stump.",
"You can put up to ten potatoes, cabbages or onions in vegetable sacks, although you can't have a mix in the same sack.",
"You can put up to five tomatoes, strawberries, apples, bananas or oranges into a fruit basket, although you can't have a mix in the same basket.",
"If you want to make your own sacks and baskets you'll need to use the loom that's near the Farming shop in Falador. If you're a good enough ${if (player!!.isMale) "craftsman" else "craftswoman"}, that is.",
"You can buy all the farming tools from farming shops, which can be found close to the allotments.",
"Hops are good for brewing ales. I believe there's a brewery up in Keldagrim somewhere, and I've heard rumours that a place called Phasmatys used to be good for that type of thing. 'Fore they all died, of course.",
)
npcl(FacialExpression.NEUTRAL, advice.random()).also { stage = START_DIALOGUE }
}
}
return true
}
fun checkPatch(player: Player,fPatch: FarmingPatch){
if(fPatch.getPatchFor(player).isWeedy()){
npc("You don't have anything planted in that patch.","Plant something and I might agree to look after it for you.").also { stage = END_DIALOGUE }
} else if(fPatch.getPatchFor(player).isGrown()){
npc("That patch is already fully grown!","I don't know what you want me to do with it!").also { stage = END_DIALOGUE }
} else if(fPatch.getPatchFor(player).protectionPaid) {
npc("Are you alright? You've already", "paid me for that.").also { stage = END_DIALOGUE }
} else {
end()
player.dialogueInterpreter.open(FarmerPayOptionDialogue(fPatch.getPatchFor(player)),npc)
}
fun openPayGardenerDialogue(player: Player, fPatch: FarmingPatch) {
end()
openDialogue(player, FarmerPayOptionDialogue(fPatch.getPatchFor(player)), npc)
}
override fun getIds(): IntArray {

View file

@ -22,7 +22,7 @@ public class ManDialoguePlugin extends DialoguePlugin {
/**
* The NPC ids that use this dialogue plugin.
*/
private static final int[] NPC_IDS = {1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 351, 352, 353, 354, 359, 360, 361, 362, 363, 726, 727, 728, 729, 730, 1086, 2675, 2776, 3224, 3225, 3227, 5923, 5924,};
private static final int[] NPC_IDS = {1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 1086, 2675, 2776, 3224, 3225, 3227, 5923, 5924,};
public ManDialoguePlugin() {
}

View file

@ -36,17 +36,21 @@ public final class ClanInterfacePlugin extends ComponentPlugin {
switch (component.getId()) {
case 589:
switch (button) {
case 9:
if (player.getInterfaceManager().getComponent(590) != null) {
player.getPacketDispatch().sendMessage("Please close the interface you have open before using 'Clan Setup'");
case 9:
if (player.getInterfaceManager().getComponent(590) != null) {
player.getPacketDispatch().sendMessage("Please close the interface you have open before using 'Clan Setup'");
return true;
}
ClanRepository.openSettings(player);
return true;
}
ClanRepository.openSettings(player);
return true;
case 14:
player.getDetails().getCommunication().toggleLootshare(player);
return true;
case 14:
if (player.getIronmanManager().checkRestriction()) {
return false;
}
player.getDetails().getCommunication().toggleLootshare(player);
return true;
}
break;
case 590:
final ClanRepository clan = ClanRepository.get(player.getName(), true);

View file

@ -10,6 +10,7 @@ import core.plugin.Initializable
import core.plugin.Plugin
import core.tools.Log
import org.rs09.consts.Sounds
import content.data.Quests
/**
* Represents the experience interface.
@ -78,15 +79,15 @@ class ExperienceInterface() : ComponentPlugin() {
}
private fun checkHerblore(player: Player): Boolean{
return (player.questRepository.isComplete("Druidic Ritual"))
return (player.questRepository.isComplete(Quests.DRUIDIC_RITUAL))
}
private fun checkSummoning(player: Player): Boolean{
return player.questRepository.isComplete("Wolf Whistle")
return player.questRepository.isComplete(Quests.WOLF_WHISTLE)
}
private fun checkRunecrafting(player: Player): Boolean{
return player.questRepository.isComplete("Rune Mysteries")
return player.questRepository.isComplete(Quests.RUNE_MYSTERIES)
}
companion object {

View file

@ -2,109 +2,130 @@ package content.global.handlers.iface
import core.api.*
import core.game.event.FairyRingDialEvent
import core.game.component.Component
import core.game.interaction.InterfaceListener
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.tools.RandomFunction
import core.game.interaction.InterfaceListener
import core.game.world.GameWorld
val RING_1 = arrayOf('a','d','c','b')
val RING_2 = arrayOf('i','l','k','j')
val RING_3 = arrayOf('p','s','r','q')
import content.data.Quests
/**
* Handles the fairy ring interface
* @author Ceikry
*/
class FairyRingInterface : InterfaceListener {
val RINGS = 734
val TRAVEL_LOG = 735
companion object {
const val RINGS_IFACE = 734
const val LOG_IFACE_ID = 735
const val VARP_F_RING = 816
const val VB_LOG_SORT_ORDER = 4618
const val VB_RING_1 = 2341
const val VB_RING_2 = 2342
const val VB_RING_3 = 2343
val RING_1 = arrayOf('a','d','c','b')
val RING_2 = arrayOf('i','l','k','j')
val RING_3 = arrayOf('p','s','r','q')
}
override fun defineInterfaceListeners() {
onOpen(RINGS){player, _ ->
player.interfaceManager.openSingleTab(Component(TRAVEL_LOG))
player.setAttribute("fr:ring1", 0)
player.setAttribute("fr:ring2", 0)
player.setAttribute("fr:ring3", 0)
FairyRing.drawLog(player)
onOpen(RINGS_IFACE){ player, _ ->
openSingleTab(player, LOG_IFACE_ID)
saveVarp(player, VARP_F_RING)
return@onOpen true
}
onClose(RINGS){player, _ ->
closeTabInterface(player)
player.removeAttribute("fr:ring1")
player.removeAttribute("fr:ring2")
player.removeAttribute("fr:ring3")
setVarp(player, 816, 0)
onOpen(LOG_IFACE_ID){ player, _ ->
drawLog(player)
return@onOpen true
}
onClose(RINGS_IFACE){ player, _ ->
closeTabInterface(player)
return@onClose true
}
on(RINGS){player, _, _, buttonID, _, _ ->
if(player.getAttribute("fr:time",0L) > System.currentTimeMillis()) return@on true
var delayIncrementer = 1750L
on(RINGS_IFACE){ player, _, _, buttonID, _, _ ->
when(buttonID){
23 -> delayIncrementer += increment(player,1)
25 -> delayIncrementer += increment(player,2)
27 -> delayIncrementer += increment(player,3)
23 -> increment(player,1)
25 -> increment(player,2)
27 -> increment(player,3)
24 -> decrement(player,1)
26 -> decrement(player,2)
28 -> decrement(player,3)
21 -> confirm(player)
}
player.setAttribute("fr:time",System.currentTimeMillis() + delayIncrementer)
return@on true
}
on(TRAVEL_LOG,12){player, _, _, _, _, _ ->
on(LOG_IFACE_ID,12){ player, _, _, _, _, _ ->
toggleSortOrder(player)
return@on true
}
}
private fun toggleSortOrder(player: Player): Long{
val ring1index = player.getAttribute("fr:ring1",0)
var toSet = player.getAttribute("fr:sortorder",true)
toSet = !toSet
player.setAttribute("fr:sortorder",toSet)
if(toSet) {
setVarp(player, 816, ring1index)
player.setAttribute("fr:ring2",0)
player.setAttribute("fr:ring3",0)
/**
* Draws the travel log interface
* Currently, the visited logs is a bool array in globalData. Someone should migrate this to prefs or something at some point.
* On transmit of Varp 816, which all used varbits are part of here, the CS2 is invoked to populate the codes in the log and sort them correctly.
* @param player The player to draw the interface for
*/
private fun drawLog (player: Player)
{
for (i in FairyRing.values().indices) {
/* Snowscape: show all fairy ring destinations, whether they have been there or not.
if (!player.savedData.globalData.hasTravelLog(i)) {
continue
}
*/
val ring = FairyRing.values()[i]
if (ring.childId == -1) {
continue
}
setInterfaceText(player, "<br>${ring.tip}", LOG_IFACE_ID, ring.childId)
}
return -1750L
}
fun increment(player: Player,ring: Int): Long{
val curIndex = player.getAttribute("fr:ring$ring",0)
var nextIndex = 0
if(curIndex == 3) nextIndex = 0
else if(curIndex == 1) nextIndex = 3
else if(curIndex == 2) nextIndex = 2
else nextIndex = curIndex + 1
player.setAttribute("fr:ring$ring",nextIndex)
return if (curIndex == 1) 1750L else 0L
private fun toggleSortOrder(player: Player) {
val curSort = getVarbit(player, VB_LOG_SORT_ORDER) == 0
setVarbit(player, VB_LOG_SORT_ORDER, if (curSort) 1 else 0)
drawLog(player)
}
fun increment(player: Player,ring: Int) {
val vbit = when(ring) {
1 -> VB_RING_1
2 -> VB_RING_2
3 -> VB_RING_3
else -> return
}
val curIndex = getVarbit(player, vbit)
val nextIndex: Int = if(curIndex == 3) 0
else curIndex + 1
setVarbit(player, vbit, nextIndex)
}
fun decrement(player: Player,ring: Int){
val curIndex = player.getAttribute("fr:ring$ring",0)
var nextIndex = 0
if(curIndex == 0) nextIndex = 3
else nextIndex = curIndex - 1
player.setAttribute("fr:ring$ring",nextIndex)
val vbit = when(ring) {
1 -> VB_RING_1
2 -> VB_RING_2
3 -> VB_RING_3
else -> return
}
val curIndex = getVarbit(player, vbit)
val nextIndex: Int = if(curIndex == 0) 3
else curIndex - 1
setVarbit(player, vbit, nextIndex)
}
private fun confirm(player: Player){
val ring1index = player.getAttribute("fr:ring1",0)
val ring2index = player.getAttribute("fr:ring2",0)
val ring3index = player.getAttribute("fr:ring3",0)
val ring1index = getVarbit(player, VB_RING_1)
val ring2index = getVarbit(player, VB_RING_2)
val ring3index = getVarbit(player, VB_RING_3)
val code = "${RING_1[ring1index]}${RING_2[ring2index]}${RING_3[ring3index]}"
val ring: FairyRing? = try {
FairyRing.valueOf(code.uppercase())
@ -160,7 +181,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int
AKS(Location.create(2571, 2956, 0), "Feldip Hills: Jungle Hunter area", 25),
ALQ(Location.create(3597, 3495, 0), "Morytania: Haunted Woods east of Canifis", 27) {
override fun checkAccess(player: Player) : Boolean {
return requireQuest(player, "Priest in Peril", "to use this ring.")
return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.")
}
},
ALS(Location.create(2644, 3495, 0), "Kandarin: McGrubor's Wood", 29),
@ -172,7 +193,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int
BKQ(Location.create(3041, 4532, 0), "Other realms: Enchanted Valley", 39),
BKR(Location.create(3469, 3431, 0), "Morytania: Mort Myre, south of Canifis", 40) {
override fun checkAccess(player: Player) : Boolean {
return requireQuest(player, "Priest in Peril", "to use this ring.")
return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.")
}
},
BLP(Location.create(2437, 5126, 0), "Dungeons: TzHaar area", 42),
@ -180,7 +201,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int
BLR(Location.create(2740, 3351, 0), "Kandarin: Legends' Guild", 44),
CIP(Location.create(2513, 3884, 0), "Islands: Miscellania", 46) {
override fun checkAccess(player: Player): Boolean {
return requireQuest(player, "Fremennik Trials", "to use this ring.")
return requireQuest(player, Quests.THE_FREMENNIK_TRIALS, "to use this ring.")
}
},
CIQ(Location.create(2528, 3127, 0), "Kandarin: North-west of Yanille", 47),
@ -189,7 +210,7 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int
CKR(Location.create(2801, 3003, 0), "Karamja: South of Tai Bwo Wannai Village", 56),
CKS(Location.create(3447, 3470, 0), "Morytania: Canifis", 57) {
override fun checkAccess(player: Player) : Boolean {
return requireQuest(player, "Priest in Peril", "to use this ring.")
return requireQuest(player, Quests.PRIEST_IN_PERIL, "to use this ring.")
}
},
CLP(Location.create(3082, 3206, 0), "Islands: South of Draynor Village", 58),
@ -208,23 +229,4 @@ enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int
open fun checkAccess(player: Player) : Boolean {
return true
}
companion object {
/**
* Draws the travel log.
* @param player the player.
*/
fun drawLog(player: Player) {
for (i in FairyRing.values().indices) {
if (!player.savedData.globalData.hasTravelLog(i)) {
continue
}
val ring = FairyRing.values()[i]
if (ring.childId == -1) {
continue
}
setInterfaceText(player, "<br>${ring.tip}", 735, ring.childId)
}
}
}
}

View file

@ -165,6 +165,7 @@ public final class JewelleryInterface extends ComponentPlugin {
amount = 5;
break;
case 124:
/* No need to calculate the maximum amount here, because the make() function will perform that step
if (data.name().contains("GOLD")) {
amount = player.getInventory().getAmount(new Item(GOLD_BAR));
} else {
@ -178,6 +179,8 @@ public final class JewelleryInterface extends ComponentPlugin {
amount = first;
}
}
*/
amount = 99;
break;
case 199:
final JewelleryItem d = data;

View file

@ -14,6 +14,9 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook;
import core.game.world.GameWorld;
import core.plugin.Plugin;
import content.data.Quests;
import static core.api.ContentAPIKt.hasRequirement;
/**
* Represents the magic book interface handling of non-combat spells.
* @author 'Vexia
@ -42,9 +45,44 @@ public final class MagicBookInterface extends ComponentPlugin {
? SpellBook.ANCIENT
: SpellBook.LUNAR;
// Snowscape: Allow switching between unlocked spellbooks with the magic interface sort buttons
if (spellBook == SpellBook.MODERN) {
if (button == 65 && swapSpellBook(player, SpellBook.ANCIENT)) {
return true;
} else if (button == 66 && swapSpellBook(player, SpellBook.LUNAR)) {
return true;
}
} else if (spellBook == SpellBook.ANCIENT) {
if (button == 29 && swapSpellBook(player, SpellBook.MODERN)) {
return true;
} else if (button == 31 && swapSpellBook(player, SpellBook.LUNAR)) {
return true;
}
} else if (spellBook == SpellBook.LUNAR) {
if (button == 40 && swapSpellBook(player, SpellBook.MODERN)) {
return true;
} else if (button == 41 && swapSpellBook(player, SpellBook.ANCIENT)) {
return true;
}
}
SpellListeners.run(button, SpellListener.NONE, SpellUtils.getBookFromInterface(component.getId()),player,null);
boolean result = MagicSpell.castSpell(player, spellBook, button, player);
return result;
}
// Snowscape custom function
private boolean swapSpellBook(final Player player, SpellBook spellBook) {
if (spellBook == SpellBook.ANCIENT && !hasRequirement(player, Quests.DESERT_TREASURE)) {
return false;
} else if (spellBook == SpellBook.LUNAR && !hasRequirement(player, Quests.LUNAR_DIPLOMACY)) {
return false;
}
player.getSpellBookManager().setSpellBook(spellBook);
player.getSpellBookManager().update(player);
return true;
}
}

View file

@ -9,6 +9,7 @@ import core.plugin.Initializable;
import core.plugin.Plugin;
import static core.api.ContentAPIKt.hasRequirement;
import content.data.Quests;
/**
* Represents the prayer interface.
@ -27,7 +28,7 @@ public final class PrayerTabInterface extends ComponentPlugin {
public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) {
final PrayerType type = PrayerType.get(button);
if (type == PrayerType.CHIVALRY || type == PrayerType.PIETY)
if (!hasRequirement(player, "King's Ransom"))
if (!hasRequirement(player, Quests.KINGS_RANSOM))
return true;
if (type == null) {
return true;

View file

@ -10,12 +10,13 @@ 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.questName.equals(questName, true) }.firstOrNull() ?: return
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<String>()
@ -27,7 +28,7 @@ object QuestTabUtils {
closeInterface(player)
for (req in unmetReqs) {
if (req is QuestReq)
questList.add(req.questReq.questName)
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
@ -82,163 +83,163 @@ object QuestTabUtils {
openInterface(player, Components.QUESTJOURNAL_SCROLL_275)
}
fun getNameForButton (button: Int) : String {
val name = when (button) {
10 -> "Myths of the White Lands"
11 -> "Myths of the White Lands"
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 -> "Black Knights' Fortress"
14 -> "Cook's Assistant"
15 -> "Demon Slayer"
16 -> "Doric's Quest"
17 -> "Dragon Slayer"
18 -> "Ernest the Chicken"
19 -> "Goblin Diplomacy"
20 -> "Imp Catcher"
21 -> "The Knight's Sword"
22 -> "Pirate's Treasure"
23 -> "Prince Ali Rescue"
24 -> "The Restless Ghost"
25 -> "Romeo & Juliet"
26 -> "Rune Mysteries"
27 -> "Sheep Shearer"
28 -> "Shield of Arrav"
29 -> "Vampire Slayer"
30 -> "Witch's Potion"
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 -> "Animal Magnetism"
33 -> "Between a Rock..."
34 -> "Big Chompy Bird Hunting"
35 -> "Biohazard"
36 -> "Cabin Fever"
37 -> "Clock Tower"
38 -> "Contact!"
39 -> "Zogre Flesh Eaters"
40 -> "Creature of Fenkenstrain"
41 -> "Darkness of Hallowvale"
42 -> "Death to the Dorgeshuun"
43 -> "Death Plateau"
44 -> "Desert Treasure"
45 -> "Devious Minds"
46 -> "The Dig Site"
47 -> "Druidic Ritual"
48 -> "Dwarf Cannon"
49 -> "Eadgar's Ruse"
50 -> "Eagles' Peak"
51 -> "Elemental Workshop I"
52 -> "Elemental Workshop II"
53 -> "Enakhra's Lament"
54 -> "Enlightened Journey"
55 -> "The Eyes of Glouphrie"
56 -> "Fairytale I - Growing Pains"
57 -> "Fairytale II - Cure a Queen"
58 -> "Family Crest"
59 -> "The Feud"
60 -> "Fight Arena"
61 -> "Fishing Contest"
62 -> "Forgettable Tale..."
63 -> "The Fremennik Trials"
64 -> "Waterfall Quest"
65 -> "Garden of Tranquillity"
66 -> "Gertrude's Cat"
67 -> "Ghosts Ahoy"
68 -> "The Giant Dwarf"
69 -> "The Golem"
70 -> "The Grand Tree"
71 -> "The Hand in the Sand"
72 -> "Haunted Mine"
73 -> "Hazeel Cult"
74 -> "Heroes' Quest"
75 -> "Holy Grail"
76 -> "Horror from the Deep"
77 -> "Icthlarin's Little Helper"
78 -> "In Aid of the Myreque"
79 -> "In Search of the Myreque"
80 -> "Jungle Potion"
81 -> "Legend's Quest"
82 -> "Lost City"
83 -> "The Lost Tribe"
84 -> "Lunar Diplomacy"
85 -> "Making History"
86 -> "Merlin's Crystal"
87 -> "Monkey Madness"
88 -> "Monk's Friend"
89 -> "Mountain Daughter"
90 -> "Mourning's End Part I"
91 -> "Mourning's End Part II"
92 -> "Murder Mystery"
93 -> "My Arm's Big Adventure"
94 -> "Nature Spirit"
95 -> "Observatory Quest"
96 -> "One Small Favour"
97 -> "Plague City"
98 -> "Priest in Peril"
99 -> "Rag and Bone Man"
100 -> "Ratcatchers"
101 -> "Recipe for Disaster"
102 -> "Recruitment Drive"
103 -> "Regicide"
104 -> "Roving Elves"
105 -> "Royal Trouble"
106 -> "Rum Deal"
107 -> "Scorpion Catcher"
108 -> "Sea Slug"
109 -> "The Slug Menace"
110 -> "Shades of Mort'ton"
111 -> "Shadow of the Storm"
112 -> "Sheep Herder"
113 -> "Shilo Village"
114 -> "A Soul's Bane"
115 -> "Spirits of the Elid"
116 -> "Swan Song"
117 -> "Tai Bwo Wannai Trio"
118 -> "A Tail of Two Cats"
119 -> "Tears of Guthix"
120 -> "Temple of Ikov"
121 -> "Throne of Miscellania"
122 -> "The Tourist Trap"
123 -> "Witch's House"
124 -> "Tree Gnome Village"
125 -> "Tribal Totem"
126 -> "Troll Romance"
127 -> "Troll Stronghold"
128 -> "Underground Pass"
129 -> "Wanted!"
130 -> "Watchtower"
131 -> "Cold War"
132 -> "The Fremennik Isles"
133 -> "Tower of Life"
134 -> "The Great Brain Robbery"
135 -> "What Lies Below"
136 -> "Olaf's Quest"
137 -> "Another Slice of H.A.M"
138 -> "Dream Mentor"
139 -> "Grim Tales"
140 -> "King's Ransom"
141 -> "The Path of Glouphrie"
142 -> "Back to my Roots"
143 -> "Land of the Goblins"
144 -> "Dealing with Scabaras"
145 -> "Wolf Whistle"
146 -> "As a First Resort..."
147 -> "Catapult Construction"
148 -> "Kennith's Concerns"
149 -> "Legacy of Seergaze"
150 -> "Perils of Ice Mountain"
151 -> "TokTz-Ket-Dill"
152 -> "Smoking Kills"
153 -> "Rocking Out"
154 -> "Spirit of Summer"
155 -> "Meeting History"
156 -> "All Fired Up"
157 -> "Summer's End"
158 -> "Defender of Varrock"
159 -> "Swept Away"
160 -> "While Guthix Sleeps"
161 -> "In Pyre Need"
162 -> "Myths of the White Lands"
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 name
return quest as String
}
}

View file

@ -49,7 +49,7 @@ public class SmeltingInterface extends ComponentPlugin {
* @author 'Vexia
*/
public enum BarButton {
BRONZE_1(16, Bar.BRONZE, 1), BRONZE_5(15, Bar.BRONZE, 5), BRONZE_10(14, Bar.BRONZE, 10), BRONZE_X(13, Bar.BRONZE, -1), BLURITE_1(20, Bar.BLURITE, 1), BLURITE_5(19, Bar.BLURITE, 5), BLURITE_10(18, Bar.BLURITE, 10), BLURITE_X(17, Bar.BLURITE, -1), IRON_1(24, Bar.IRON, 1), IRON_5(23, Bar.IRON, 5), IRON_10(22, Bar.IRON, 10), IRON_X(21, Bar.IRON, -1), SILVER_1(28, Bar.SILVER, 1), SILVER_5(27, Bar.SILVER, 5), SILVER_10(26, Bar.SILVER, 10), SILVER_X(25, Bar.SILVER, -1), STEEL_1(32, Bar.STEEL, 1), STEEL_5(31, Bar.STEEL, 5), STEEL_10(30, Bar.STEEL, 10), STEEL_X(29, Bar.STEEL, -1), GOLD_1(36, Bar.GOLD, 1), GOLD_5(35, Bar.GOLD, 5), GOLD_10(34, Bar.GOLD, 10), GOLD_X(33, Bar.GOLD, -1), MITHRIL_1(40, Bar.MITHRIL, 1), MITHRIL_5(39, Bar.MITHRIL, 5), MITHRIL_10(38, Bar.MITHRIL, 10), MITHRIL_X(37, Bar.MITHRIL, -1), ADAMANT_1(44, Bar.ADAMANT, 1), ADAMANT_5(43, Bar.ADAMANT, 5), ADAMANT_10(42, Bar.ADAMANT, 10), ADAMANT_X(41, Bar.ADAMANT, -1), RUNE_1(48, Bar.RUNITE, 1), RUNE_5(47, Bar.RUNITE, 5), RUNE_10(46, Bar.RUNITE, 10), RUNE_X(45, Bar.RUNITE, -1);
BRONZE_1(16, Bar.BRONZE, 1), BRONZE_5(15, Bar.BRONZE, 5), BRONZE_10(14, Bar.BRONZE, 30), BRONZE_X(13, Bar.BRONZE, -1), BLURITE_1(20, Bar.BLURITE, 1), BLURITE_5(19, Bar.BLURITE, 5), BLURITE_10(18, Bar.BLURITE, 30), BLURITE_X(17, Bar.BLURITE, -1), IRON_1(24, Bar.IRON, 1), IRON_5(23, Bar.IRON, 5), IRON_10(22, Bar.IRON, 30), IRON_X(21, Bar.IRON, -1), SILVER_1(28, Bar.SILVER, 1), SILVER_5(27, Bar.SILVER, 5), SILVER_10(26, Bar.SILVER, 30), SILVER_X(25, Bar.SILVER, -1), STEEL_1(32, Bar.STEEL, 1), STEEL_5(31, Bar.STEEL, 5), STEEL_10(30, Bar.STEEL, 30), STEEL_X(29, Bar.STEEL, -1), GOLD_1(36, Bar.GOLD, 1), GOLD_5(35, Bar.GOLD, 5), GOLD_10(34, Bar.GOLD, 30), GOLD_X(33, Bar.GOLD, -1), MITHRIL_1(40, Bar.MITHRIL, 1), MITHRIL_5(39, Bar.MITHRIL, 5), MITHRIL_10(38, Bar.MITHRIL, 30), MITHRIL_X(37, Bar.MITHRIL, -1), ADAMANT_1(44, Bar.ADAMANT, 1), ADAMANT_5(43, Bar.ADAMANT, 5), ADAMANT_10(42, Bar.ADAMANT, 30), ADAMANT_X(41, Bar.ADAMANT, -1), RUNE_1(48, Bar.RUNITE, 1), RUNE_5(47, Bar.RUNITE, 5), RUNE_10(46, Bar.RUNITE, 30), RUNE_X(45, Bar.RUNITE, -1);
/**
* Constructs a new {@code BarButton} {@code Object}.

View file

@ -112,7 +112,7 @@ public class GrandExchangeInterface extends ComponentPlugin {
GrandExchangeOffer offer;
GrandExchangeRecords records = GrandExchangeRecords.getInstance(player);
if (index > -1 && (offer = records.getOffer(records.getOfferRecords()[index])) != null) {
StockMarket.withdraw(player, offer, slot >> 1);
StockMarket.withdraw(player, offer, slot >> 1, opcode);
}
return true;
}

View file

@ -98,7 +98,7 @@ class StockMarket : InterfaceListener {
209,211 -> if (openedOffer == null){
SystemLogger.logGE("[WARN] Player tried to withdraw item with null openedOffer!")
return@on false
} else withdraw(player, openedOffer, (button - 209) shr 1)
} else withdraw(player, openedOffer, (button - 209) shr 1, op)
190 -> confirmOffer(player, tempOffer, openedIndex).also { return@on true }
194 -> player.interfaceManager.openChatbox(Components.OBJDIALOG_389)
203 -> abortOffer(player, openedOffer)
@ -342,7 +342,7 @@ class StockMarket : InterfaceListener {
}
@JvmStatic
fun withdraw(player: Player, offer: GrandExchangeOffer, index: Int)
fun withdraw(player: Player, offer: GrandExchangeOffer, index: Int, op: Int)
{
val item = offer.withdraw[index]
if(item == null)
@ -351,20 +351,32 @@ class StockMarket : InterfaceListener {
return
}
if(hasSpaceFor(player, item))
{
addItem(player, item.id, item.amount)
}
else
{
val note = item.noteChange
if(note == -1 || !hasSpaceFor(player, Item(note, item.amount)))
{
playAudio(player, Sounds.GE_TRADE_ERROR_4039)
sendMessage(player, "You do not have enough room in your inventory.")
return
when (op) {
// withdraw notes
155 -> {
val note = item.noteChange
if (note == -1) {
sendMessage(player, "This item cannot be noted")
return
}
if (hasSpaceFor(player, Item(note, item.amount))) {
addItem(player, note, item.amount)
} else {
playAudio(player, Sounds.GE_TRADE_ERROR_4039)
sendMessage(player, "You do not have enough room in your inventory.")
return
}
}
// withdraw items
196 -> {
if (hasSpaceFor(player, item)) {
addItem(player, item.id, item.amount)
} else {
playAudio(player, Sounds.GE_TRADE_ERROR_4039)
sendMessage(player, "You do not have enough room in your inventory.")
return
}
}
else addItem(player, note, item.amount)
}
offer.withdraw[index] = null

View file

@ -13,6 +13,7 @@ import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Items
import org.rs09.consts.Scenery
import org.rs09.consts.Sounds
import content.data.Quests
@Suppress("unused")
class EctophialListener : InteractionListener {
@ -37,7 +38,7 @@ class EctophialListener : InteractionListener {
}
on(Items.ECTOPHIAL_4251, IntType.ITEM, "empty") { player, node ->
if (!hasRequirement(player, "Ghosts Ahoy"))
if (!hasRequirement(player, Quests.GHOSTS_AHOY))
return@on true
if (player.isTeleBlocked) {
@ -45,28 +46,29 @@ class EctophialListener : InteractionListener {
return@on true
}
delayEntity(player, 10)
queueScript(player, 0, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
sendMessage(player, "You empty the ectoplasm onto the ground around your feet...")
playAudio(player, 4580)
visualize(player, emptyAnimation, emptyGraphics)
replaceSlot(player, node.asItem().slot, Item(Items.ECTOPHIAL_4252), Item(Items.ECTOPHIAL_4251))
return@queueScript delayScript(player, emptyAnimation.duration)
}
1 -> {
teleport(player, Location.create(3658, 3517, 0), TeleportType.ECTOPHIAL)
sendMessage(player, "...and the world changes around you.")
return@queueScript delayScript(player, 9)
}
2 -> {
face(player, Location(3659, 3519, 0))
refillEctophial(player)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
closeAllInterfaces(player)
queueScript(player, 0, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
sendMessage(player, "You empty the ectoplasm onto the ground around your feet...")
playAudio(player, 4580)
visualize(player, emptyAnimation, emptyGraphics)
replaceSlot(player, node.asItem().slot, Item(Items.ECTOPHIAL_4252), Item(Items.ECTOPHIAL_4251))
return@queueScript delayScript(player, emptyAnimation.duration)
}
1 -> {
teleport(player, Location.create(3658, 3517, 0), TeleportType.ECTOPHIAL)
sendMessage(player, "...and the world changes around you.")
return@queueScript delayScript(player, 9)
}
2 -> {
face(player, Location(3659, 3519, 0))
refillEctophial(player)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
}
return@on true
}
}

View file

@ -10,12 +10,14 @@ import org.rs09.consts.Sounds
class EnchantJewelleryTabListener : InteractionListener {
private val LVL_1_ENCHANT = mapOf(
Items.BLUE_DRAGONHIDE_1751 to Items.PLAIN_SATCHEL_10877,
Items.SAPPHIRE_RING_1637 to Items.RING_OF_RECOIL_2550,
Items.SAPPHIRE_NECKLACE_1656 to Items.GAMES_NECKLACE8_3853,
Items.SAPPHIRE_AMULET_1694 to Items.AMULET_OF_MAGIC_1727,
Items.SAPPHIRE_BRACELET_11072 to Items.BRACELET_OF_CLAY_11074
)
private val LVL_2_ENCHANT = mapOf(
Items.GREEN_DRAGONHIDE_1753 to Items.GREEN_SATCHEL_10878,
Items.EMERALD_RING_1639 to Items.RING_OF_DUELLING8_2552,
Items.EMERALD_NECKLACE_1658 to Items.BINDING_NECKLACE_5521,
Items.EMERALD_AMULET_1696 to Items.AMULET_OF_DEFENCE_1729,
@ -23,6 +25,7 @@ class EnchantJewelleryTabListener : InteractionListener {
)
private val LVL_3_ENCHANT = mapOf(
Items.RED_DRAGONHIDE_1749 to Items.RED_SATCHEL_10879,
Items.RUBY_RING_1641 to Items.RING_OF_FORGING_2568,
Items.RUBY_NECKLACE_1660 to Items.DIGSITE_PENDANT_5_11194,
Items.RUBY_AMULET_1698 to Items.AMULET_OF_STRENGTH_1725,
@ -30,6 +33,7 @@ class EnchantJewelleryTabListener : InteractionListener {
)
private val LVL_4_ENCHANT = mapOf(
Items.DAGANNOTH_HIDE_6155 to Items.GOLD_SATCHEL_10881,
Items.DIAMOND_RING_1643 to Items.RING_OF_LIFE_2570,
Items.DIAMOND_NECKLACE_1662 to Items.PHOENIX_NECKLACE_11090,
Items.DIAMOND_AMULET_1700 to Items.AMULET_OF_POWER_1731,
@ -37,6 +41,7 @@ class EnchantJewelleryTabListener : InteractionListener {
)
private val LVL_5_ENCHANT = mapOf(
Items.SUQAH_HIDE_9080 to Items.RUNE_SATCHEL_10882,
Items.DRAGONSTONE_RING_1645 to Items.RING_OF_WEALTH4_14646,
Items.DRAGON_NECKLACE_1664 to Items.SKILLS_NECKLACE4_11105,
Items.DRAGONSTONE_AMMY_1702 to Items.AMULET_OF_GLORY4_1712,
@ -44,6 +49,7 @@ class EnchantJewelleryTabListener : InteractionListener {
)
private val LVL_6_ENCHANT = mapOf(
Items.BLACK_DRAGONHIDE_1747 to Items.BLACK_SATCHEL_10880,
Items.ONYX_RING_6575 to Items.RING_OF_STONE_6583,
Items.ONYX_NECKLACE_6577 to Items.BERSERKER_NECKLACE_11128,
Items.ONYX_AMULET_6581 to Items.AMULET_OF_FURY_6585,
@ -52,6 +58,7 @@ class EnchantJewelleryTabListener : InteractionListener {
override fun defineListeners() {
on(IntType.ITEM, "break") {player, node ->
closeAllInterfaces(player)
delayEntity(player, 1)
queueScript(player, strength = QueueStrength.SOFT) {

View file

@ -1,5 +1,6 @@
package content.global.handlers.item
import content.global.skill.slayer.SlayerUtils
import core.api.*
import content.global.skill.slayer.Tasks
import org.rs09.consts.Items
@ -41,7 +42,8 @@ class EnchantedGemDialogue() : DialogueFile() {
if(getSlayerTask(player!!) == Tasks.JAD) {
npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill TzTok-Jad!")
} else {
npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill ${getSlayerTaskName(player!!)}s; only ${getSlayerTaskKillsRemaining(player!!)} more to go.")
npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're currently assigned to kill ${SlayerUtils.pluralise(
getSlayerTaskName(player!!))}; only ${getSlayerTaskKillsRemaining(player!!)} more to go.")
}
setVarp(player!!, 2502, getSlayerTaskFlags(player!!) shr 4)
stage = 1

View file

@ -53,17 +53,17 @@ class ExplorersRingPlugin : InteractionListener {
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 ->
//sendDialogue(player, "Choose the item that you wish to convert to coins.")
//addDialogueAction (player) {_,_ ->
sendItemSelect (player, "Cast Low Level Alchemy on:") { slot, optionIndex ->
val item = player.inventory[slot]
if (item == null)
return@sendItemSelect
if (!ModernListeners().alchemize(player, item, false))
if (!ModernListeners().alchemize(player, item, false, explorersRing = true))
return@sendItemSelect
getStoreFile()[player.username.lowercase() + ":alchs"] = remaining - 1
}
}
//}
return@on true
}

View file

@ -8,6 +8,9 @@ import org.rs09.consts.Items
import core.game.dialogue.DialogueFile
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import core.game.node.Node
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
class GodBookListeners : InteractionListener {
@ -20,16 +23,42 @@ class GodBookListeners : InteractionListener {
openDialogue(player, HOLY_DIALOGUE(BOOK.SARA))
return@on true
}
on(GB_ZAMORAK, IntType.ITEM, "preach"){ player, _ ->
openDialogue(player, HOLY_DIALOGUE(BOOK.ZAM))
return@on true
}
on(GB_GUTHIX, IntType.ITEM, "preach"){ player, _ ->
openDialogue(player, HOLY_DIALOGUE(BOOK.GUTHIX))
return@on true
}
fun bless(player: Player, node: Node, product: Int) {
if (player.skills.getStaticLevel(Skills.PRAYER) < 50) {
sendMessage(player, "You need a Prayer level of at least 50 in order to do this.")
} else if (player.skills.prayerPoints < 4) {
sendMessage(player, "You need at least 4 prayer points in order to do this.")
} else {
sendMessage(player, "You bless the ${node.asItem().name.lowercase()}.")
player.skills.decrementPrayerPoints(4.0)
replaceSlot(player, node.asItem().slot, Item(product), node.asItem())
}
}
onUseWith(IntType.ITEM, GB_SARADOMIN, Items.UNBLESSED_SYMBOL_1716) { player, _, symbol ->
bless(player, symbol, Items.HOLY_SYMBOL_1718)
return@onUseWith true
}
onUseWith(IntType.ITEM, GB_ZAMORAK, Items.UNPOWERED_SYMBOL_1722) { player, _, symbol ->
bless(player, symbol, Items.UNHOLY_SYMBOL_1724)
return@onUseWith true
}
onUseWith(IntType.ITEM, GB_GUTHIX, Items.UNBLESSED_SYMBOL_1716) { player, _, symbol ->
bless(player, symbol, Items.HOLY_SYMBOL_1718)
return@onUseWith true
}
onUseWith(IntType.ITEM, GB_GUTHIX, Items.UNPOWERED_SYMBOL_1722) { player, _, symbol ->
bless(player, symbol, Items.UNHOLY_SYMBOL_1724)
return@onUseWith true
}
}
internal enum class BOOK {
@ -129,6 +158,5 @@ class GodBookListeners : InteractionListener {
}
})
}
}
}
}

View file

@ -27,7 +27,7 @@ public class GodBookPlugin extends OptionHandler {
for (GodBook book : GodBook.values()) {
book.getDamagedBook().getDefinition().getHandlers().put("option:check", this);
}
ClassScanner.definePlugins(new PageHandler(), new GodBookItem(), new SymbolBlessHandler());
ClassScanner.definePlugins(new PageHandler(), new GodBookItem());
return this;
}
@ -51,77 +51,6 @@ public class GodBookPlugin extends OptionHandler {
return true;
}
/**
* Handles the blessing of a symbol with a god book.
* @author Vexia
*/
public class SymbolBlessHandler extends UseWithHandler {
/**
* Constructs a new {@code SymbolBlessHandler} {@code Object}
*/
public SymbolBlessHandler() {
super(1716);
}
@Override
public Plugin<Object> 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

View file

@ -62,6 +62,7 @@ class GrandSeedPodHandler : InteractionListener {
}
if (opt == "squash") {
closeAllInterfaces(player)
visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN)
delayEntity(player, 12)
queueScript(player, 3, QueueStrength.SOFT) {stage: Int ->
@ -93,4 +94,4 @@ class GrandSeedPodHandler : InteractionListener {
return@on true
}
}
}
}

View file

@ -5,24 +5,34 @@ import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.link.TeleportManager
import core.game.node.item.Item
import core.game.node.entity.player.Player
import core.game.world.repository.Repository
class HouseTeleTabOptionListener : InteractionListener {
override fun defineListeners() {
val homeTabID = 8013
on(homeTabID, IntType.ITEM, "break") {player, node ->
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
//Snowscape: teleport to the last house you entered, if possible
var owner: Player
val lastHouseOwner = Repository.getPlayerByName(player.getAttribute("lasthouseentered", null))
if (lastHouseOwner != null && !lastHouseOwner.getHouseManager().isLocked()) {
owner = lastHouseOwner
} else {
var hasHouse = player.houseManager.location.exitLocation != null
if (!hasHouse) {
sendMessage( player, "You must have a house to teleport to before attempting that.")
return@on false
}
owner = player
}
closeInterface(player)
lock(player, 5)
if (inInventory(player, node.id)) {
player.houseManager.preEnter(player, false)
val location = player.houseManager.getEnterLocation()
owner.houseManager.preEnter(player, false)
val location = owner.houseManager.getEnterLocation()
if (teleport(player, location, TeleportManager.TeleportType.TELETABS)) {
removeItem(player, Item(node.id, 1))
player.houseManager.postEnter(player, false)
owner.houseManager.postEnter(player, false)
}
}
return@on true

View file

@ -4,6 +4,7 @@ import core.api.*
import core.game.node.entity.player.link.quest.QuestRepository
import org.rs09.consts.Items
import core.game.interaction.InteractionListener
import content.data.Quests
class ItemQuestRequirementListener : InteractionListener {
@ -106,7 +107,7 @@ class ItemQuestRequirementListener : InteractionListener {
/*
onEquip(fremennikIslesEquipment) { player, _ ->
if (!isQuestComplete(player, "Fremennik Isles")) {
if (!isQuestComplete(player, Quests.THE_FREMENNIK_ISLES)) {
sendMessage(player, "You must have completed The Fremennik Isles to equip this.")
return@onEquip false
}
@ -114,7 +115,7 @@ class ItemQuestRequirementListener : InteractionListener {
}
onEquip(fremennikIslesDuringQuestEquipment){ player, _ ->
if (questStage(player, "Fremennik Isles") > 0) {
if (questStage(player, Quests.THE_FREMENNIK_ISLES) > 0) {
sendMessage(player, "You must have started The Fremennik Isles to equip this.")
return@onEquip false
}
@ -123,23 +124,23 @@ class ItemQuestRequirementListener : InteractionListener {
*/
onEquip(fremennikTrialsEquipment) { player, _ ->
return@onEquip hasRequirement(player, "Fremennik Trials")
return@onEquip hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)
}
onEquip(fremennikIslesEquipment) {player, _ ->
return@onEquip hasRequirement(player, "The Fremennik Isles")
return@onEquip hasRequirement(player, Quests.THE_FREMENNIK_ISLES)
}
onEquip(avasBackpacks){ player, _ ->
return@onEquip hasRequirement(player, "Animal Magnetism")
return@onEquip hasRequirement(player, Quests.ANIMAL_MAGNETISM)
}
onEquip(lostCityWeapons){ player, _ ->
return@onEquip hasRequirement(player, "Lost City")
return@onEquip hasRequirement(player, Quests.LOST_CITY)
}
onEquip(Items.CAPE_OF_LEGENDS_1052) { player, _ ->
return@onEquip hasRequirement(player, "Legend's Quest")
return@onEquip hasRequirement(player, Quests.LEGENDS_QUEST)
}
onEquip(questCapes) { player, _ ->
@ -152,84 +153,84 @@ class ItemQuestRequirementListener : InteractionListener {
}
onEquip(Items.WOLFBANE_2952){ player, _ ->
return@onEquip hasRequirement(player, "Priest in Peril")
return@onEquip hasRequirement(player, Quests.PRIEST_IN_PERIL)
}
onEquip(Items.ANCIENT_MACE_11061){ player, _ ->
return@onEquip hasRequirement(player, "Another Slice of H.A.M")
return@onEquip hasRequirement(player, Quests.ANOTHER_SLICE_OF_HAM)
}
onEquip(Items.ANCIENT_STAFF_4675){ player, _ ->
return@onEquip hasRequirement(player, "Desert Treasure")
return@onEquip hasRequirement(player, Quests.DESERT_TREASURE)
}
onEquip(Items.ELEMENTAL_SHIELD_2890) { player, _ ->
return@onEquip hasRequirement(player, "Elemental Workshop I")
return@onEquip hasRequirement(player, Quests.ELEMENTAL_WORKSHOP_I)
}
onEquip(crystalEquipment){ player, _ ->
return@onEquip hasRequirement(player, "Roving Elves")
return@onEquip hasRequirement(player, Quests.ROVING_ELVES)
}
onEquip(dragonSlayerEquipment) {player, _ ->
return@onEquip hasRequirement(player, "Dragon Slayer")
return@onEquip hasRequirement(player, Quests.DRAGON_SLAYER)
}
onEquip(Items.DRAGON_SCIMITAR_4587) {player, _ ->
return@onEquip hasRequirement(player, "Monkey Madness")
return@onEquip hasRequirement(player, Quests.MONKEY_MADNESS)
}
onEquip(Items.GLOVES_7462) {player, _ ->
return@onEquip hasRequirement(player, "Recipe for Disaster")
return@onEquip hasRequirement(player, Quests.RECIPE_FOR_DISASTER)
}
onEquip(Items.SLAYER_HELMET_13263) {player, _ ->
return@onEquip hasRequirement(player, "Smoking Kills")
return@onEquip hasRequirement(player, Quests.SMOKING_KILLS)
}
onEquip (Items.DRAGON_HALBERD_3204) {player, _ ->
return@onEquip hasRequirement(player, "Regicide")
return@onEquip hasRequirement(player, Quests.REGICIDE)
}
onEquip (Items.CLIMBING_BOOTS_3105) {player, _ ->
return@onEquip hasRequirement(player, "Death Plateau")
return@onEquip hasRequirement(player, Quests.DEATH_PLATEAU)
}
onEquip (godBooks) {player, _ ->
return@onEquip hasRequirement(player, "Horror from the Deep")
return@onEquip hasRequirement(player, Quests.HORROR_FROM_THE_DEEP)
}
onEquip (pharaohScepters) {player, _ ->
return@onEquip hasRequirement(player, "Icthlarin's Little Helper")
return@onEquip hasRequirement(player, Quests.ICTHLARINS_LITTLE_HELPER)
}
onEquip (Items.DRAGON_SQ_SHIELD_1187) {player, _ ->
//because I know people won't believe it: https://runescape.wiki/w/Dragon_sq_shield?oldid=899636
return@onEquip hasRequirement(player, "Legend's Quest")
return@onEquip hasRequirement(player, Quests.LEGENDS_QUEST)
}
onEquip (initiateArmour) {player, _ ->
return@onEquip hasRequirement(player, "Recruitment Drive")
return@onEquip hasRequirement(player, Quests.RECRUITMENT_DRIVE)
}
onEquip (proselyteArmour) {player, _ ->
return@onEquip hasRequirement(player, "The Slug Menace")
return@onEquip hasRequirement(player, Quests.THE_SLUG_MENACE)
}
onEquip (spiritShields) {player, _ ->
return@onEquip hasRequirement(player, "Summer's End")
return@onEquip hasRequirement(player, Quests.SUMMERS_END)
}
onEquip (Items.DRAGON_MACE_1434) {player, _ ->
return@onEquip hasRequirement(player, "Heroes' Quest")
return@onEquip hasRequirement(player, Quests.HEROES_QUEST)
}
onEquip (Items.DRAGON_BATTLEAXE_1377) {player, _ ->
return@onEquip hasRequirement(player, "Heroes' Quest")
return@onEquip hasRequirement(player, Quests.HEROES_QUEST)
}
onEquip (Items.DARKLIGHT_6746) {player, _ ->
return@onEquip hasRequirement(player, "Shadow of the Storm")
return@onEquip hasRequirement(player, Quests.SHADOW_OF_THE_STORM)
}
}
}

View file

@ -12,6 +12,9 @@ import core.plugin.Initializable;
import core.plugin.Plugin;
import core.plugin.ClassScanner;
import core.tools.RandomFunction;
import org.rs09.consts.Sounds;
import static core.api.ContentAPIKt.playAudio;
/**
* Handles a morph item.
@ -65,6 +68,7 @@ public class MorphItemPlugin implements Plugin<Object> {
*/
private void morph(Player player, Item item) {
int morphId = item.getId() == 6583 ? 2626 : EASTER_EGG_IDS[RandomFunction.random(EASTER_EGG_IDS.length)];
playAudio(player, 1520);
player.getInterfaceManager().close();
player.getAppearance().transformNPC(morphId);
player.getInterfaceManager().removeTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);

View file

@ -8,6 +8,7 @@ import core.game.node.entity.player.Player;
import core.plugin.Initializable;
import core.plugin.Plugin;
import content.region.morytania.quest.naturespirit.NSUtils;
import content.data.Quests;
/**
* Handles the Silver Sickle (b) to collect Mort Myre Fungus.
@ -28,7 +29,7 @@ public final class SilverSicklePlugin extends OptionHandler {
switch (option) {
case "operate":
case "cast bloom":
if(player.getQuestRepository().getQuest("Nature Spirit").getStage(player) >= 75) {
if(player.getQuestRepository().getQuest(Quests.NATURE_SPIRIT).getStage(player) >= 75) {
player.getPacketDispatch().sendAnimation(9021);
NSUtils.castBloom(player);
} else {

View file

@ -0,0 +1,183 @@
package content.global.handlers.item
import core.api.*
import core.game.container.Container
import content.global.skill.summoning.familiar.BurdenContainerListener
import core.game.node.Node
import core.game.node.entity.player.Player
//import core.game.node.entity.player.info.login.PlayerSaver
import core.game.node.item.Item
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import org.rs09.consts.Items
import core.game.component.Component
import core.game.component.CloseEvent
import core.game.container.access.InterfaceContainer
import content.global.handlers.scenery.BankBoothListener
import content.global.handlers.npc.BankerNPC
/**
* Listener for satchels from Creature Creation, repurposed for Snowscape
* They are repurposed as item pouches, using the summoning familiar inventory interface
* @author
*/
class SnowscapeSatchelListener : InteractionListener {
companion object {
val plainSatchelId = 10877
val greenSatchelId = 10878
val redSatchelId = 10879
val blackSatchelId = 10880
val goldSatchelId = 10881
val runeSatchelId = 10882
val satchelIds = intArrayOf(plainSatchelId,greenSatchelId,redSatchelId,blackSatchelId,goldSatchelId,runeSatchelId)
private fun transferItem(player: Player, satchelId: Int, item: Item, amount: Int, withdraw: Boolean, asNote: Boolean = false) {
if (withdraw == false && !isAllowed(player, satchelId, unnote(item).getId())) {
player.sendMessage(item.getName() + " cannot be stored in the " + Item(satchelId).getName() + ".")
return
}
if (withdraw == true && asNote == false && satchelId == plainSatchelId) {
player.sendMessage("This satchel can only be emptied by a banker. Use it on a bank booth to get the items.")
return
}
val to = if (withdraw) player.getInventory() else getSatchel(player, satchelId)
val from = if (withdraw) getSatchel(player, satchelId) else player.getInventory()
val maximum = if (asNote) to.getMaximumAdd(note(item)) else to.getMaximumAdd(unnote(item))
if (maximum < 1 && withdraw == false) {
player.sendMessage("There is not enough space for the " + item.getName() + " in the satchel.")
return
}
val finalAmount = kotlin.comparisons.minOf(amount, from.getAmount(item), maximum)
val finalItem = Item(item.getId(), finalAmount)
if (finalAmount > 0 && from.remove(finalItem)) {
if (asNote) to.add(note(finalItem)) else to.add(unnote(finalItem))
}
}
private fun emptySatchel(player: Player, satchelId: Int, asNote: Boolean = false) {
if (asNote == false && satchelId == plainSatchelId) {
player.sendMessage("This satchel can only be emptied by a banker. Use it on a Bank Booth to get the items.")
return
}
val satchel = getSatchel(player, satchelId)
repeat(satchel.capacity()) { index ->
var item = satchel.get(index)
if (item != null) {
transferItem(player, satchelId, item, satchel.getAmount(item), true, asNote)
}
}
}
//Function to handle constructing the correct interfaces when the satchel is opened
private fun openSatchel(player: Player, node: Node) {
val satchelId = node.asItem().getId()
val satchel = getSatchel(player, satchelId)
if (satchel.getListeners().count() < 1) satchel.register(BurdenContainerListener(player))
player.getInterfaceManager().open(Component(671)).setCloseEvent(CloseEvent { player, component ->
player.getInterfaceManager().closeSingleTab()
removeAttribute(player, "openSatchel")
return@CloseEvent true
}
)
setAttribute(player, "openSatchel", satchelId)
satchel.shift()
player.getInterfaceManager().openSingleTab(Component(665))
InterfaceContainer.generateItems(player, player.getInventory().toArray(), arrayOf("Store-X","Store-All","Store-10","Store-5","Store-1"),665,0,7,4,93)
InterfaceContainer.generateItems(player, satchel.toArray(), arrayOf("Withdraw-X","Withdraw-All","Withdraw-10","Withdraw-5","Withdraw-1"),671,27,5,6,30)
}
// This is called by BurdenInterfacePlugin.java when the player interacts with the familiar interface and the "openSatchel" attribute exists
fun satchelInterfaceAction(player: Player, component: Component, opcode: Int, button: Int, slot: Int, itemId: Int) {
val satchelId = getAttribute(player, "openSatchel", 0)
val withdraw = component.getId() == 671
val container = if (withdraw) getSatchel(player, satchelId) else player.getInventory()
val item = if (slot >= 0 && slot < container.capacity()) container.get(slot) else null
if (item == null && button != 29) return
when (opcode) {
155 -> if (button == 29) emptySatchel(player, satchelId) else transferItem(player, satchelId, item!!, 1, withdraw)
196 -> transferItem(player, satchelId, item!!, 5, withdraw)
124 -> transferItem(player, satchelId, item!!, 10, withdraw)
199 -> transferItem(player, satchelId, item!!, container.getAmount(item), withdraw)
234 -> sendInputDialogue(player, true, "Enter the amount:") { value -> transferItem(player, satchelId, item!!, value as Int, withdraw) }
9 -> player.sendMessage(item!!.getDefinition().getExamine())
}
}
fun getSatchel(player: Player, satchelId: Int): Container {
var satchel: Container? = null
when (satchelId) {
plainSatchelId -> satchel = player.plainSatchel
greenSatchelId -> satchel = player.greenSatchel
redSatchelId -> satchel = player.redSatchel
blackSatchelId -> satchel = player.blackSatchel
goldSatchelId -> satchel = player.goldSatchel
runeSatchelId -> satchel = player.runeSatchel
}
return satchel!!
}
// Checks if the item is allowed in the satchel. The NPCDropTables.java function calls this on satchels the player is carrying.
fun isAllowed(player: Player, satchelId: Int, itemId: Int): Boolean {
when (satchelId) {
plainSatchelId -> return itemId !in satchelIds
greenSatchelId -> return (Item(itemId).getName().contains(" seed") || Item(itemId).getName().contains("Grimy") || Item(itemId).getName().contains("Clean "))
redSatchelId -> return itemId in intArrayOf(*(12158..12168).toList().toIntArray()) || Item(itemId).getName().equals("Clue scroll")
blackSatchelId -> return itemId in intArrayOf(995)
goldSatchelId -> return itemId in intArrayOf(436,438,440,442,444,446,447,449,451,453,668,2892,2349,2351,2353,2355,2357,2359,2361,2363,2365,1601,1603,1605,1607,1609,1611,1613,1615,1617,1619,1621,1623,1625,1627,1629,1631,6571,6573)
runeSatchelId -> return itemId in intArrayOf(556,555,557,554,558,559,564,562,9075,561,563,560,565,566,4694,4695,4696,4697,4698,4699)
}
return false
}
}
override fun defineListeners() {
onUseAnyWith(IntType.ITEM, *satchelIds) { player, used, with ->
val satchelId = with.asItem().getId()
transferItem(player, satchelId, used.asItem(), player.getInventory().getAmount(used.asItem()),false)
return@onUseAnyWith true
}
on(satchelIds, IntType.ITEM, "inspect","operate") { player, node ->
openSatchel(player, node)
return@on true
}
on(satchelIds, IntType.ITEM, "empty") { player, node ->
emptySatchel(player, node.asItem().getId())
return@on true
}
// BankBoothListener values are public but BankChestListener values are not, so rather than edit the BankChestListener file I have copied the IDs here. 12309 is the Culinomancer's chest.
val bankChests = intArrayOf(3194,4483,10562,14382,16695,16696,21301,27662,27663)
val bankUseWiths = intArrayOf(*BankBoothListener.BANK_BOOTHS, 12309, *bankChests)
onUseWith(IntType.SCENERY, satchelIds, *bankUseWiths) { player, used, with ->
emptySatchel(player, used.asItem().getId(), true)
return@onUseWith true
}
//6362 is the Ourania Altar banker.
val bankers = intArrayOf(*BankerNPC.NPC_IDS, *BankerNPC.SPECIAL_NPC_IDS, 6362)
onUseWith(IntType.NPC, satchelIds, *bankers) { player, used, with ->
emptySatchel(player, used.asItem().getId(), true)
return@onUseWith true
}
}
}

View file

@ -0,0 +1,146 @@
package content.global.handlers.item
import core.api.*
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import core.game.world.map.Location
import core.game.world.map.build.DynamicRegion
import core.game.world.map.zone.ZoneRestriction
import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics
import core.game.node.entity.player.link.TeleportManager
import core.game.dialogue.*
import core.tools.START_DIALOGUE
import org.rs09.consts.Items
import org.rs09.consts.Sounds
import core.game.component.Component
/**
* Listener for the strange teleorb object. Normally this object is part of the While Guthix Sleeps quest, but it has been repurposed.
* The new purpose is a custom teleport artifact, allowing players to save a location and teleport back to it.
* @author
*/
class SnowscapeTeleorb : InteractionListener {
// The teleorb item id
val teleorb = 14534
// How many law runes are required for one teleport charge
val runesPerTeleportNormal = 20
val runesPerTeleportBoosted = 2
override fun defineListeners() {
on(teleorb, IntType.ITEM, "inspect") { player, node ->
inspect(player, node)
return@on true
}
on(teleorb, IntType.ITEM, "activate") { player, node ->
activate(player, node)
return@on true
}
}
private fun inspect(player: Player, node: Node) {
openDialogue(player,SnowscapeTeleorbDialogue())
}
private fun activate(player: Player, node: Node) {
val charges = getAttribute(player, "teleorb:charges",0) as Int
var runesPerTeleport = if (getAttribute(player, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal
if (charges >= runesPerTeleport) {
val destination = getAttribute(player, "teleorb:location", Location.create(3236,3218,0))
player.getTeleporter().send(destination, TeleportManager.TeleportType.ENTRANA_MAGIC_DOOR)
setAttribute(player, "/save:teleorb:charges", charges - runesPerTeleport)
player.sendMessage("Charges remaining: " + ((getAttribute(player, "teleorb:charges",0) as Int) / runesPerTeleport))
rewardXP(player, Skills.MAGIC, 15.0 * runesPerTeleport)
} else {
player.sendMessage("<col=ff0000>The teleorb is out of charges.</col>")
}
}
}
class SnowscapeTeleorbDialogue : DialogueFile() {
// How many law runes are required for one teleport charge
val runesPerTeleportNormal = 20
val runesPerTeleportBoosted = 2
val teleorb = 14534
val lawRune = 563
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
START_DIALOGUE -> options("Update Location","Recharge","Show Info").also {stage++}
1 -> when (buttonID) {
1 -> player?.let {
end()
val region = it.getViewport().getRegion()
if (region == null || region is DynamicRegion || it.getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) {
it.sendMessage("This location cannot be saved!")
} else {
setAttribute(it, "/save:teleorb:location", it.getLocation())
sendInputDialogue(it, false, "Location saved. Enter a description of this location:",) { value ->
setAttribute(it, "/save:teleorb:description", value as String)
}
it.sendMessage("Location saved.")
it.graphics(Graphics(343))
it.animate(Animation(1818))
playAudio(it, Sounds.TELE_OTHER_CAST_199)
}
}
2 -> options("Recharge with Law Runes","Recharge with additional orbs").also {stage++}
3 -> player?.let {
end()
var runesPerTeleport = if (getAttribute(it, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal
it.sendMessage("Current saved location: " + getAttribute(it, "teleorb:description", "Unknown").replace("_"," "))
it.sendMessage("Charges remaining: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport))
}
}
2 -> when (buttonID) {
1 -> player?.let {
end()
var runesPerTeleport = if (getAttribute(it, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal
sendInputDialogue(it, true, "Each charge requires $runesPerTeleport Law Runes. How many runes would you like to use?",) { value ->
var amount = kotlin.math.min(amountInInventory(it,lawRune), value as Int)
if (removeItem(it, Item(lawRune, amount))) {
it.sendMessage("You charge the teleorb with $amount Law Runes.")
amount += (getAttribute(it, "teleorb:charges",0) as Int)
setAttribute(it, "/save:teleorb:charges", amount)
it.sendMessage("Current charges: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport))
it.graphics(Graphics(141,96))
it.animate(Animation(722))
playAudio(it, Sounds.LUNAR_EMBUE_RUNES_2888) //Yes, Imbue is mispelled in the sounds list.
}
}
}
2 -> player?.let {
end()
var runesPerTeleport = if (getAttribute(it, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal
sendInputDialogue(it, true, "Each additional orb adds one charge. How many would you like to use?",) { value ->
var amount = kotlin.math.min(amountInInventory(it,teleorb) - 1, value as Int)
if (amount > 0 && removeItem(it, Item(teleorb, amount))) {
it.sendMessage("You destroy $amount teleorbs, fusing their energy into one.")
amount = amount * runesPerTeleport
amount += (getAttribute(it, "teleorb:charges",0) as Int)
setAttribute(it, "/save:teleorb:charges", amount)
it.sendMessage("Current charges: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport))
it.graphics(Graphics(141,96))
it.animate(Animation(722))
playAudio(it, Sounds.LUNAR_EMBUE_RUNES_2888)
}
}
}
}
}
}
}

View file

@ -1,39 +1,55 @@
package content.global.handlers.item
import core.api.inInventory
import core.api.removeItem
import core.api.teleport
import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.Companion.ARDOUGNE_TELE_ATTRIBUTE
import core.api.*
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager
import core.game.node.item.Item
import core.game.world.map.Location
import core.api.hasRequirement;
import content.data.Quests
import core.cache.def.impl.ItemDefinition;
class TeleTabsListener : InteractionListener {
enum class TeleTabs(val item: Int, val location: Location, val exp: Double, val requirementCheck: (Player) -> Boolean = { true }) {
ADDOUGNE_TELEPORT(8011, Location.create(2662, 3307, 0), 61.0, {
player -> hasRequirement(player, "Plague City");
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),
ASTRAL_ALTAR_TELEPORT(13611, Location.create(2156, 3862, 0), 0.0, {
player -> hasRequirement(player, Quests.LUNAR_DIPLOMACY);
}),
BLOOD_ALTAR_TELEPORT(13610, Location.create(3559, 9778, 0), 0.0, {
player -> hasRequirement(player, Quests.LEGACY_OF_SEERGAZE);
}),
BODY_ALTAR_TELEPORT(13604, Location.create(3055, 3443, 0), 0.0),
CAMELOT_TELEPORT(8010, Location.create(2757, 3477, 0), 55.5),
CHAOS_ALTAR_TELEPORT(13606, Location.create(3058, 3593, 0), 0.0),
COSMIC_ALTAR_TELEPORT(13605, Location.create(2411, 4380, 0), 0.0),
DEATH_ALTAR_TELEPORT(13609, Location.create(1863, 4639, 0), 0.0),
COSMIC_ALTAR_TELEPORT(13605, Location.create(2411, 4380, 0), 0.0, {
player -> hasRequirement(player, Quests.LOST_CITY);
}),
DEATH_ALTAR_TELEPORT(13609, Location.create(1863, 4639, 0), 0.0, {
player -> hasRequirement(player, Quests.MOURNINGS_END_PART_II);
}),
EARTH_ALTAR_TELEPORT(13602, Location.create(3304, 3472, 0), 0.0),
FALADOR_TELEPORT(8009, Location.create(2966, 3380, 0), 47.0),
FIRE_ALTAR_TELEPORT(13603, Location.create(3311, 3252, 0), 0.0),
LAW_ALTAR_TELEPORT(13608, Location.create(2857, 3378, 0), 0.0),
LAW_ALTAR_TELEPORT(13608, Location.create(2857, 3378, 0), 0.0, {
player -> if (!ItemDefinition.canEnterEntrana(player)) (player.sendMessage("The power of Saradomin prevents you from taking armour or weaponry to Entrana.") !is Unit) else true; // ugly way of doing this, but the sendMessage function returns a Unit type, so by using !is Unit it becomes false.
}),
LUMBRIDGE_TELEPORT(8008, Location.create(3222, 3218, 0), 41.0),
MIND_ALTAR_TELEPORT(13600, Location.create(2979, 3510, 0), 0.0),
NATURE_ALTAR_TELEPORT(13607, Location.create(2868, 3013, 0), 0.0),
TELEKINETIC_GRAB(8022, Location.create(2836, 3285, 0), 0.0, {
player -> hasRequirement(player, Quests.DRAGON_SLAYER);
}),
VARROCK_TELEPORT(8007, Location.create(3212, 3423, 0), 35.00),
WATCH_TOWER_TELEPORT(8012, Location.create(2548, 3114, 0), 68.00),
WATCH_TOWER_TELEPORT(8012, Location.create(2548, 3114, 0), 68.00, {
player -> hasRequirement(player, Quests.WATCHTOWER);
}),
WATER_ALTAR_TELEPORT(13601, Location.create(3182, 3162, 0), 0.0);
companion object {
@ -51,9 +67,17 @@ class TeleTabsListener : InteractionListener {
val tabEnum = TeleTabs.forId(tab)
if (tabEnum != null && inInventory(player,tab)) {
val tabloc = tabEnum.location
if (inInventory(player, tab) && tabEnum.requirementCheck(player)) {
if (teleport(player, tabloc, TeleportManager.TeleportType.TELETABS)) {
removeItem(player, Item(node.id, 1))
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.")
}
}
}
}

View file

@ -14,6 +14,7 @@ import core.game.world.map.zone.impl.WildernessZone;
import core.plugin.Plugin;
import static core.api.ContentAPIKt.hasRequirement;
import content.data.Quests;
/**
* Represents the rotten potato plugin.
@ -34,7 +35,7 @@ public final class TeleportCrystalPlugin extends OptionHandler {
@Override
public boolean handle(Player player, Node node, String option) {
if (!hasRequirement(player, "Mourning's End Part I"))
if (!hasRequirement(player, Quests.MOURNINGS_END_PART_I))
return true;
if (!WildernessZone.checkTeleport(player, 20)) {
player.getPacketDispatch().sendMessage("The crystal is unresponsive.");

View file

@ -6,6 +6,13 @@ import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Items
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import kotlinx.coroutines.delay
import org.rs09.consts.Sounds
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class ToyListeners : InteractionListener {
companion object {
@ -14,10 +21,12 @@ class ToyListeners : InteractionListener {
private val MARIONETTE_WALK = Animation(3004)
private val MARIONETTE_BOW = Animation(3005)
private val MARIONETTE_DANCE = Animation(3006)
private val MARIONETTE_GFX = arrayOf(intArrayOf(507, 508, 509, 510), intArrayOf(511, 512, 513, 514), intArrayOf(515, 516, 517, 518))
private val MARIONETTE_GFX =
arrayOf(intArrayOf(507, 508, 509, 510), intArrayOf(511, 512, 513, 514), intArrayOf(515, 516, 517, 518))
private val SNOWGLOBE_SHAKE = Animation(7535) //Initial Shake
private val SNOWGLOBE_HOLDFACE = Animation(7536) //Immediately after shake, player holds the snow globe to face
private val SNOWGLOBE_INTERFACE = 659 //After HOLDFACE this interface is displayed, player either clicks 'continue' for inv of snowballs, or 'close' for no snowballs
private val SNOWGLOBE_INTERFACE =
659 //After HOLDFACE this interface is displayed, player either clicks 'continue' for inv of snowballs, or 'close' for no snowballs
private val SNOWGLOBE_DOWNFAST = Animation(7537) //Used when player hit 'close' on the interface
private val SNOWGLOBE_DOWNSLOW = Animation(7538) //Used when the player hit 'continue' on the interface
private val SNOWGLOBE_STOMP = Animation(7528) //When player hits continue this animation plays
@ -34,7 +43,7 @@ class ToyListeners : InteractionListener {
}
override fun defineListeners() {
on(Items.CHOCATRICE_CAPE_12634, IntType.ITEM, "operate"){ player, _ ->
on(Items.CHOCATRICE_CAPE_12645, IntType.ITEM, "operate") { player, _ ->
lockInteractions(player, 2)
visualize(player, 8903, 1566)
return@on true
@ -44,18 +53,18 @@ class ToyListeners : InteractionListener {
val index = MARIONETTES.indexOf(marionette.id)
lockInteractions(player, 2)
when(getUsedOption(player)) {
when (getUsedOption(player)) {
"jump" -> visualize(player, MARIONETTE_JUMP, MARIONETTE_GFX[index][0])
"walk" -> visualize(player, MARIONETTE_WALK, MARIONETTE_GFX[index][1])
"bow" -> visualize(player, MARIONETTE_BOW, MARIONETTE_GFX[index][2])
"bow" -> visualize(player, MARIONETTE_BOW, MARIONETTE_GFX[index][2])
"dance" -> visualize(player, MARIONETTE_DANCE, MARIONETTE_GFX[index][3])
}
return@on true
}
on(Items.REINDEER_HAT_10507, IntType.ITEM, "operate"){ player, _ ->
on(Items.REINDEER_HAT_10507, IntType.ITEM, "operate") { player, _ ->
lockInteractions(player, 2)
animate(player, 5059)
visualize(player, 5059, 859)
return@on true
}
@ -64,7 +73,7 @@ class ToyListeners : InteractionListener {
animate(player, SNOWGLOBE_SHAKE)
runTask(player, 3) {
animate(player, SNOWGLOBE_HOLDFACE)
runTask(player){
runTask(player) {
openInterface(player, SNOWGLOBE_INTERFACE)
}
}
@ -80,17 +89,17 @@ class ToyListeners : InteractionListener {
return@on true
}
on(Items.TOY_KITE_12844, IntType.ITEM, "fly"){ player, _ ->
on(Items.TOY_KITE_12844, IntType.ITEM, "fly","operate") { player, _ ->
lockInteractions(player, 2)
animate(player, TOY_KITE_FLY)
return@on true
}
on(Items.YO_YO_4079, IntType.ITEM, "play", "loop", "walk", "crazy"){ player, _ ->
on(Items.YO_YO_4079, IntType.ITEM, "play", "loop", "walk", "crazy") { player, _ ->
val option = getUsedOption(player)
lockInteractions(player, 2)
when(option) {
when (option) {
"play" -> animate(player, YOYO_PLAY)
"loop" -> animate(player, YOYO_LOOP)
"walk" -> animate(player, YOYO_WALK)
@ -99,11 +108,11 @@ class ToyListeners : InteractionListener {
return@on true
}
on(Items.ZOMBIE_HEAD_6722, IntType.ITEM, "talk-at", "display", "question"){ player, _ ->
on(Items.ZOMBIE_HEAD_6722, IntType.ITEM, "talk-at", "display", "question") { player, _ ->
val option = getUsedOption(player)
lockInteractions(player, 2)
when(option) {
when (option) {
"talk-at" -> {
animate(player, ZOMBIE_HEAD_TALK_AT)
sendChat(player, "Alas!")
@ -116,5 +125,12 @@ class ToyListeners : InteractionListener {
}
return@on true
}
on(Items.RUBBER_CHICKEN_4566, IntType.ITEM, "operate", "Dance") { player, _ ->
lockInteractions(player, 2)
visualize(player, 1835, -1)
playJingle(player, 99);
playAudio(player, 355, 100);
return@on true
}
}
}

View file

@ -7,7 +7,7 @@ class CrystalEquipmentRegister : StartupListener {
val shield: Array<Int> = 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<Int> = arrayOf(Items.NEW_CRYSTAL_BOW_4212, Items.CRYSTAL_BOW_FULL_4214, Items.CRYSTAL_BOW_9_10_4215, Items.CRYSTAL_BOW_8_10_4216, Items.CRYSTAL_BOW_7_10_4217, Items.CRYSTAL_BOW_6_10_4218, Items.CRYSTAL_BOW_5_10_4219, Items.CRYSTAL_BOW_4_10_4220, Items.CRYSTAL_BOW_3_10_4221, Items.CRYSTAL_BOW_2_10_4222, Items.CRYSTAL_BOW_1_10_4223, Items.CRYSTAL_SEED_4207)
override fun startup() {
EquipmentDegrader.registerSet(250, shield)
EquipmentDegrader.registerSet(250, bow)
EquipmentDegrader.registerSet(1200, shield)
EquipmentDegrader.registerSet(1200, bow)
}
}

View file

@ -57,7 +57,7 @@ public final class AncientMaceSpecialHandler extends MeleeSwingHandler implement
state.setStyle(CombatStyle.MELEE);
int hit = 0;
if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.1, 0.98)) {
hit = RandomFunction.random(calculateHit(entity, victim, 1));
hit = RandomFunction.random(calculateHit(entity, victim, 1) + 1);
if (entity.getSkills().getPrayerPoints() < entity.getSkills().getStaticLevel(5)) {
entity.getSkills().setPrayerPoints(entity.getSkills().getPrayerPoints() + hit);
}

View file

@ -59,13 +59,10 @@ public final class BackstabSpecialHandler extends MeleeSwingHandler implements P
}
state.setStyle(CombatStyle.MELEE);
int hit = 0;
double accuracy = 1.0;
if (!victim.getProperties().getCombatPulse().isAttacking()) {
accuracy = 1.75;
}
if (isAccurateImpact(entity, victim, CombatStyle.MELEE, accuracy, 0.98)) {
hit = RandomFunction.random(calculateHit(entity, victim, 1.0));
victim.getSkills().updateLevel(Skills.DEFENCE, -hit / 10, 0);
if (!victim.getProperties().getCombatPulse().isAttacking() || isAccurateImpact(entity, victim, CombatStyle.MELEE)) {
hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1);
if (victim.getSkills().getStaticLevel(Skills.DEFENCE) >= victim.getSkills().getDynamicLevels()[Skills.DEFENCE])
victim.getSkills().updateLevel(Skills.DEFENCE, -hit, 0);
}
state.setEstimatedHit(hit);
return 1;

View file

@ -1,9 +1,7 @@
package content.global.handlers.item.equipment.special;
import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.DeathTask;
import core.game.node.entity.combat.*;
import core.game.node.entity.combat.ImpactHandler.HitsplatType;
import core.game.node.entity.impl.Animator.Priority;
import core.game.node.entity.impl.Projectile;
@ -17,7 +15,6 @@ import core.game.world.update.flag.context.Graphics;
import core.plugin.Initializable;
import core.plugin.Plugin;
import core.tools.RandomFunction;
import core.game.node.entity.combat.RangeSwingHandler;
import core.game.world.GameWorld;
import core.game.world.repository.Repository;
import org.rs09.consts.Sounds;
@ -34,6 +31,13 @@ import static core.api.ContentAPIKt.playGlobalAudio;
@Initializable
public final class ChainhitSpecialHandler extends RangeSwingHandler implements Plugin<Object> {
/**
* Constructs a new {@code ChainhitSpecialHandler} {@code Object}.
*/
public ChainhitSpecialHandler() {
super(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE);
}
/**
* The sp::ecial energy required.
*/
@ -159,7 +163,7 @@ public final class ChainhitSpecialHandler extends RangeSwingHandler implements P
public boolean pulse() {
BattleState bs = new BattleState(entity, e);
bs.setMaximumHit(calculateHit(player, e, 1.0));
bs.setEstimatedHit(RandomFunction.RANDOM.nextInt(bs.getMaximumHit()));
bs.setEstimatedHit(RandomFunction.random(bs.getMaximumHit() + 1));
handleHit(victim, e, player, bs);
ChainhitSpecialHandler.super.visualizeImpact(player, e, bs);
return true;

View file

@ -31,13 +31,6 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler {
*/
private static final Graphics END_GRAPHIC = new Graphics(157, 96);
/**
* Constructs a new {@code ChinchompaSwingHandler} {@code Object}.
*/
public ChinchompaSwingHandler() {
super(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE);
}
@Override
public int swing(Entity entity, Entity victim, BattleState state) {
boolean multi = entity.getProperties().isMultiZone() && victim.getProperties().isMultiZone();
@ -72,7 +65,7 @@ public final class ChinchompaSwingHandler extends RangeSwingHandler {
s.setStyle(CombatStyle.RANGE);
int hit = 0;
if (isAccurateImpact(entity, e, CombatStyle.RANGE)) {
hit = RandomFunction.random(calculateHit(entity, e, 1.0));
hit = RandomFunction.random(calculateHit(entity, e, 1.0) + 1);
}
s.setEstimatedHit(hit);
}

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