diff --git a/build-logic/src/main/kotlin/ScriptMetadataTask.kt b/build-logic/src/main/kotlin/ScriptMetadataTask.kt index 7b9c261ee6..421ea76da5 100644 --- a/build-logic/src/main/kotlin/ScriptMetadataTask.kt +++ b/build-logic/src/main/kotlin/ScriptMetadataTask.kt @@ -67,7 +67,9 @@ abstract class ScriptMetadataTask : DefaultTask() { continue } val psiFile: KtFile = instance.findFile(localFile) as KtFile - val classes = psiFile.collectDescendantsOfType() + val classes = psiFile + .collectDescendantsOfType() + .filter { clazz -> clazz.superTypeListEntries.any { type -> type.text == "Script" } } val packageName = psiFile.packageFqName.asString() if (change.changeType == ChangeType.MODIFIED) { for (name in classes.map { it.name }) { @@ -79,10 +81,8 @@ abstract class ScriptMetadataTask : DefaultTask() { if (change.changeType == ChangeType.MODIFIED || change.changeType == ChangeType.ADDED) { for (ktClass in classes) { val className = ktClass.name ?: "Anonymous" - if (ktClass.superTypeListEntries.any { it.text == "Script" }) { - lines.add("$packageName.$className") - scripts++ - } + lines.add("$packageName.$className") + scripts++ } } } diff --git a/data/area/misthalin/varrock/cooks_guild/cooks_guild.vars.toml b/data/area/misthalin/varrock/cooks_guild/cooks_guild.vars.toml new file mode 100644 index 0000000000..4926f9c72f --- /dev/null +++ b/data/area/misthalin/varrock/cooks_guild/cooks_guild.vars.toml @@ -0,0 +1,7 @@ +[pie_task] +format = "string" +persist = true + +[pie_task_amount] +format = "int" +persist = true diff --git a/data/area/misthalin/varrock/varrock.npcs.toml b/data/area/misthalin/varrock/varrock.npcs.toml index f3027502a6..f445aa4752 100644 --- a/data/area/misthalin/varrock/varrock.npcs.toml +++ b/data/area/misthalin/varrock/varrock.npcs.toml @@ -312,7 +312,7 @@ id = 5066 [valaine] id = 536 -shop = "varrock_general_store" +shop = "valaines_shop_of_champions" examine = "A champion saleswoman!" [scavvo] diff --git a/data/client/structs.toml b/data/client/structs.toml index b896ab9126..4cae890e80 100644 --- a/data/client/structs.toml +++ b/data/client/structs.toml @@ -241,7 +241,7 @@ tan_your_hide_task = 1562 handicrafts_task = 1563 handy_dandy_task = 1531 just_cant_get_the_staff_task = 1532 -the_restless_ghost_task = 1249 +the_restless_ghost = 1249 click_your_heels_three_times_task = 1533 come_and_have_a_go_task = 1534 reach_out_and_touch_someone_task = 1535 diff --git a/data/entity/obj/door/door.objs.toml b/data/entity/obj/door/door.objs.toml index fbc49a8c05..a25fc049d9 100644 --- a/data/entity/obj/door/door.objs.toml +++ b/data/entity/obj/door/door.objs.toml @@ -372,11 +372,11 @@ examine = "A door to a grand place." id = 1575 examine = "A door to a grand place." -[door_28_opened] +[door_champions_guild_opened] id = 2033 examine = "This is someone's bedroom door." -[door_28_closed] +[door_champions_guild_closed] id = 1805 examine = "The door to the Champions' Guild." @@ -814,10 +814,10 @@ id = 34233 id = 2706 examine = "A nicely fitted door." -[door_81_opened] +[door_cooks_guild_opened] id = 15502 -[door_81_closed] +[door_cooks_guild_closed] id = 2712 examine = "The entrance to the Cooking Guild." @@ -2828,10 +2828,10 @@ examine = "A strong metal door." id = 26706 examine = "A strong metal door." -[door_490_opened] +[door_cooks_guild_achievement_opened] id = 26811 -[door_490_closed] +[door_cooks_guild_achievement_closed] id = 26810 examine = "The door is closed." diff --git a/data/entity/player/modal/tab/tab.varcs.toml b/data/entity/player/modal/tab/tab.varcs.toml index 9078c5b733..8316dcf7e3 100644 --- a/data/entity/player/modal/tab/tab.varcs.toml +++ b/data/entity/player/modal/tab/tab.varcs.toml @@ -2,4 +2,4 @@ id = 168 format = "list" default = "Inventory" -values = [ "CombatStyles", "TaskSystem", "Stats", "QuestJournals", "Inventory", "WornEquipment", "PrayerList", "MagicSpellbook", "FriendsList", "IgnoreList", "Options", "ClanChat", "Emotes", "MusicPlayer", "Notes" ] +values = [ "CombatStyles", "TaskSystem", "Stats", "QuestJournals", "Inventory", "WornEquipment", "PrayerList", "MagicSpellbook", "FollowerDetails", "FriendsList", "IgnoreList", "ClanChat", "Options", "Emotes", "MusicPlayer", "Notes" ] diff --git a/game/src/main/kotlin/content/area/asgarnia/rimmington/CraftingGuild.kt b/game/src/main/kotlin/content/area/asgarnia/rimmington/CraftingGuild.kt index 6dd4f5d8e9..0d6e960607 100644 --- a/game/src/main/kotlin/content/area/asgarnia/rimmington/CraftingGuild.kt +++ b/game/src/main/kotlin/content/area/asgarnia/rimmington/CraftingGuild.kt @@ -8,6 +8,7 @@ import content.entity.player.dialogue.type.npc import content.entity.player.dialogue.type.player import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.level.Level import world.gregs.voidps.engine.entity.character.player.skill.level.Level.has import world.gregs.voidps.engine.entity.character.player.skill.level.Level.hasMax import world.gregs.voidps.engine.inv.equipment @@ -49,8 +50,9 @@ class CraftingGuild : Script { } option("That's fine.") { inventory.transaction { + val trimmed = Skill.entries.any { it != Skill.Crafting && levels.getMax(it) >= Level.MAX_LEVEL } remove("coins", 99000) - add("crafting_cape") + add("crafting_cape${if (trimmed) "_t" else ""}") add("crafting_hood") } when (inventory.transaction.error) { diff --git a/game/src/main/kotlin/content/area/asgarnia/taverley/Kaqemeex.kt b/game/src/main/kotlin/content/area/asgarnia/taverley/Kaqemeex.kt index 0445d5cd72..d389c2b7fe 100644 --- a/game/src/main/kotlin/content/area/asgarnia/taverley/Kaqemeex.kt +++ b/game/src/main/kotlin/content/area/asgarnia/taverley/Kaqemeex.kt @@ -12,6 +12,7 @@ import world.gregs.voidps.engine.entity.character.jingle import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.name import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.level.Level import world.gregs.voidps.engine.entity.character.player.skill.level.Level.hasMax import world.gregs.voidps.engine.event.AuditLog import world.gregs.voidps.engine.inv.inventory @@ -149,7 +150,8 @@ class Kaqemeex : Script { } option("Okay, here's 99,000 coins.") { inventory.transaction { - add("herblore_cape") + val trimmed = Skill.entries.any { it != Skill.Herblore && levels.getMax(it) >= Level.MAX_LEVEL } + add("herblore_cape${if (trimmed) "_t" else ""}") add("herblore_hood") remove("coins", 99000) } diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/church/LumbridgeChurch.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/church/LumbridgeChurch.kt index abaf58d66e..3fa9e1bc77 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/church/LumbridgeChurch.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/church/LumbridgeChurch.kt @@ -74,7 +74,7 @@ class LumbridgeChurch : Script { } } - itemOnObjectOperate("muddy_skull", "coffin_restless_ghost_2") { + itemOnObjectOperate("muddy_skull", "coffin_restless_ghost_2,restless_ghost_coffin") { returnSkull() } diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/ChampionsGuild.kt b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/ChampionsGuild.kt new file mode 100644 index 0000000000..8c8fd011ae --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/ChampionsGuild.kt @@ -0,0 +1,25 @@ +package content.area.misthalin.varrock.champions_guild + +import content.entity.obj.door.enterDoor +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message + +class ChampionsGuild : Script { + init { + objectOperate("Open", "door_champions_guild_closed") { (target) -> + if (get("quest_points", 0) < 32) { + npc("guildmaster", "You have not proved yourself worthy to enter here yet.") + message("The door won't open - you need at least 32 Quest Points.") + return@objectOperate + } + val entered = tile.y >= 3363 + enterDoor(target) + if (entered) { + npc("guildmaster", "Greetings bold adventurer. Welcome to the guild of Champions.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Guildmaster.kt b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Guildmaster.kt new file mode 100644 index 0000000000..651da9fdc5 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Guildmaster.kt @@ -0,0 +1,20 @@ +package content.area.misthalin.varrock.champions_guild + +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Guildmaster : Script { + init { + npcOperate("Talk-to", "guildmaster") { + npc("Greetings!") + choice { + option("What is this place?") { + npc("This is the Champions' Guild. Only adventurers who have proved themselves worthy by gaining influence from quests are allowed in here.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Scavvo.kt b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Scavvo.kt new file mode 100644 index 0000000000..b35f5e98b0 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Scavvo.kt @@ -0,0 +1,31 @@ +package content.area.misthalin.varrock.champions_guild + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Scavvo : Script { + init { + npcOperate("Talk-to", "scavvo") { (target) -> + npc("'Ello matey! D'ya wanna buy some exciting new toys?") + choice { + option("No, toys are for kids.") { + } + option("Let's have a look then.") { + openShop(target.def["shop", ""]) + } + option("Ooh, goody goody, toys!") { + openShop(target.def["shop", ""]) + } + option("Why do you sell most rune armour but not platebodies?") { + npc("Oh, you have to complete a special quest in order to wear rune platemail. You should talk to the Guildmaster downstairs about that.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Valaine.kt b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Valaine.kt new file mode 100644 index 0000000000..8964e7bffd --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/champions_guild/Valaine.kt @@ -0,0 +1,22 @@ +package content.area.misthalin.varrock.champions_guild + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Valaine : Script { + init { + npcOperate("Talk-to", "valaine") { (target) -> + npc("Hello there. Want to have a look at what we're selling today?") + choice { + option("Yes please.") { + openShop(target.def["shop", ""]) + } + option("No thank you.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/CooksGuild.kt b/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/CooksGuild.kt new file mode 100644 index 0000000000..51ee48db51 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/CooksGuild.kt @@ -0,0 +1,37 @@ +package content.area.misthalin.varrock.cooks_guild + +import content.entity.obj.door.enterDoor +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.statement +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.equip.equipped +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot + +class CooksGuild : Script { + init { + objectOperate("Open", "door_cooks_guild_closed") { (target) -> + if (levels.get(Skill.Cooking) < 32) { + npc("head_chef", "You need a cooking level of 32 to come in here.") + return@objectOperate + } + if (equipped(EquipSlot.Hat).id != "chefs_hat" && equipped(EquipSlot.Hat).id != "cooking_hood" && equipped(EquipSlot.Cape).id != "cooking_cape" && equipped(EquipSlot.Cape).id != "cooking_cape_t") { + npc("head_chef", "You can't come in here unless you're wearing a chef's hat, or something like that.") + return@objectOperate + } + // https://youtu.be/uPZANIKxM7c?si=nLgUxCiqTr48rKCk&t=98 +// npc("My word! A master explorer of Varrock! Come in, come in! You are more than welcome in here, my friend!") + enterDoor(target) + } + + objectOperate("Open", "door_cooks_guild_achievement_closed") { (target) -> + if (levels.get(Skill.Cooking) < 99) { + statement("You must have completed the Varrock Achievement Diary Hard tasks or master the Cooking skill to gain entry.") + return@objectOperate + } + enterDoor(target) + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/HeadChef.kt b/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/HeadChef.kt new file mode 100644 index 0000000000..76d31059c1 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/HeadChef.kt @@ -0,0 +1,61 @@ +package content.area.misthalin.varrock.cooks_guild + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.level.Level +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItem.remove + +class HeadChef : Script { + init { + npcOperate("Talk-to", "head_chef") { + if (levels.get(Skill.Cooking) == 99) { + // https://youtu.be/6LuyWmDoJtg?si=_jAvyeri-gTmBEFI&t=66 + npc("Hello, welcome to the Cooking Guild. It's always great to have such an accomplished chef visit us. Say, would you be interested in a Skillcape of Cooking? They're only available to master chefs.") + choice { + option("No thanks.") + option("Yes please.") { + player("Can I buy a Skillcape of Cooking from you?") + npc("Most certainly, just as soon as you give me 99000 gold coins.") + choice { + option("That's much too expensive.") + option("Sure") { + inventory.transaction { + val trimmed = Skill.entries.any { it != Skill.Cooking && levels.getMax(it) >= Level.MAX_LEVEL } + remove("coins", 99000) + add("cooking_cape${if (trimmed) "_t" else ""}") + add("cooking_hood") + } + when (inventory.transaction.error) { + is TransactionError.Deficient -> player("I'm afraid you don't have enough coins on me at the moment.") // TODO proper message + is TransactionError.Full -> npc("I'm afraid you don't have enough inventory spaces to take the cape and hood.") // TODO proper message + TransactionError.None -> npc("Now you can use the title Master Chef.") + else -> {} + } + } + } + } + } + } else { + npc("Hello, welcome to the Cooking Guild. Only accomplished chefs and cooks are allowed in here. Feel free to use any of our facilities.") + choice { + option("Nice cape you're wearing!") { + player("Nice cape, you're wearing!") + npc("Thank you! It's my most prized possession, it's a Skillcape of Cooking; it shows that I've achieved level 99 Cooking and am one of the best chefs in the land!") + npc("If you ever achieve level 99 cooking you'll get to wear one too and doing so means you'll never burn any food!") + } + option("Thanks, bye.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/RomilyWeaklax.kt b/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/RomilyWeaklax.kt new file mode 100644 index 0000000000..c23d062460 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/cooks_guild/RomilyWeaklax.kt @@ -0,0 +1,113 @@ +package content.area.misthalin.varrock.cooks_guild + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import net.pearx.kasechange.toTitleCase +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.ui.chat.plural +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItemLimit.removeToLimit +import world.gregs.voidps.type.random + +class RomilyWeaklax : Script { + init { + npcOperate("Talk-to", "romily_weaklax") { (target) -> + npc("Hello and welcome to my pie shop, how can I help you?") + choice { + option("I'd like to buy some pies.") { + openShop(target.def["shop", ""]) + } + val pie = get("pie_task", "") + if (pie != "") { + val amount = get("pie_task_amount", 0) + option("I've got those pies you wanted.") { + var removed = 0 + inventory.transaction { + removed = removeToLimit(pie, amount) + add("coins", removed * reward(pie)) + } + if (inventory.transaction.error != TransactionError.None || removed <= 0) { + npc("Doesn't look like you have any of the $amount ${pie.toTitleCase()} I requested.") + return@option + } + val remaining = amount - removed + set("pie_task_amount", remaining) + if (remaining <= 0) { + npc("Thank you very much!") + } else { + npc("Thank you, if you could bring me the other $remaining that'd be great!") + } + } + } else { + option("Do you need any help?") { + npc("Actually I could, you see I'm running out of stock and I don't have time to bake any more pies. Would you be willing to bake me some pies? I'll pay you well for them.") + choice { + option("Sure, what do you need?") { + val id = randomPie(levels.get(Skill.Cooking)) + val amount = random.nextInt(4, 29) + set("pie_task", id) + set("pie_task_amount", amount) + npc("Great, can you bake me $amount ${id.toTitleCase().plural(amount)} please.") + } + option("Sorry, I can't help you.") + } + } + } + option("I'm good thanks.") + } + } + } + + private fun reward(pie: String) = when (pie) { + "meat_pie" -> 18 + "mud_pie" -> 67 + "apple_pie" -> 37 + "garden_pie" -> 30 + "fish_pie" -> 125 + "admiral_pie" -> 387 + "wild_pie" -> 227 + "summer_pie" -> 175 + else -> 15 + } + + private fun randomPie(level: Int): String { + val pies = buildSet { + if (level >= 10) { + add("redberry_pie") + } + if (level >= 20) { + add("meat_pie") + } + if (level >= 29) { + add("mud_pie") + } + if (level >= 30) { + add("apple_pie") + } + if (level >= 34) { + add("garden_pie") + } + if (level >= 47) { + add("fish_pie") + } + if (level >= 70) { + add("admiral_pie") + } + if (level >= 85) { + add("wild_pie") + } + if (level >= 95) { + add("summer_pie") + } + } + return pies.random(random) + } +} diff --git a/game/src/main/kotlin/content/entity/Movement.kt b/game/src/main/kotlin/content/entity/Movement.kt index f9d7f4b44c..427af2a335 100644 --- a/game/src/main/kotlin/content/entity/Movement.kt +++ b/game/src/main/kotlin/content/entity/Movement.kt @@ -9,6 +9,7 @@ import world.gregs.voidps.engine.data.definition.Areas import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.mode.EmptyMode import world.gregs.voidps.engine.entity.character.mode.PauseMode +import world.gregs.voidps.engine.entity.character.mode.interact.PlayerOnObjectInteract import world.gregs.voidps.engine.entity.character.npc.NPCs import world.gregs.voidps.engine.entity.character.player.Players import world.gregs.voidps.engine.map.collision.Collisions @@ -50,6 +51,9 @@ class Movement : Script { if (player.contains("delay")) { return@instruction } + if (player.mode is PlayerOnObjectInteract) { + player.clearAnim() + } player.closeInterfaces() player.clearWatch() player.queue.clearWeak() diff --git a/game/src/main/kotlin/content/entity/npc/shop/ShopOpen.kt b/game/src/main/kotlin/content/entity/npc/shop/ShopOpen.kt index 113104a1f4..44b84ab7e7 100644 --- a/game/src/main/kotlin/content/entity/npc/shop/ShopOpen.kt +++ b/game/src/main/kotlin/content/entity/npc/shop/ShopOpen.kt @@ -29,6 +29,7 @@ class ShopOpen(val inventoryDefinitions: InventoryDefinitions) : Script { interfaceClosed("shop") { close("item_info") close("shop_side") + sendVariable("tab") val shop = shop() if (shop.endsWith("general_store")) { GeneralStores.unbind(this, shop) diff --git a/game/src/main/kotlin/content/entity/player/modal/GameFrame.kt b/game/src/main/kotlin/content/entity/player/modal/GameFrame.kt index 0e2119ddeb..d33b49f7cb 100644 --- a/game/src/main/kotlin/content/entity/player/modal/GameFrame.kt +++ b/game/src/main/kotlin/content/entity/player/modal/GameFrame.kt @@ -44,7 +44,7 @@ class GameFrame : Script { Tab.entries.forEach { tab -> val name = tab.name.toSnakeCase() interfaceOption(name.toTitleCase(), "toplevel*:$name") { - set("tab", false, tab.name) + set("tab", tab.name) } } diff --git a/game/src/main/kotlin/content/entity/player/modal/Tab.kt b/game/src/main/kotlin/content/entity/player/modal/Tab.kt index 5925643ac5..97c2ce8a7b 100644 --- a/game/src/main/kotlin/content/entity/player/modal/Tab.kt +++ b/game/src/main/kotlin/content/entity/player/modal/Tab.kt @@ -11,10 +11,11 @@ enum class Tab { WornEquipment, PrayerList, MagicSpellbook, + FollowerDetails, FriendsList, IgnoreList, - Options, ClanChat, + Options, Emotes, MusicPlayer, Notes,