diff --git a/Server/data/ObjectParser.xml b/Server/data/ObjectParser.xml
index 053e1b5a6..74b3c658b 100644
--- a/Server/data/ObjectParser.xml
+++ b/Server/data/ObjectParser.xml
@@ -24,5 +24,13 @@
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json
index b56cf1f0a..e151475ee 100644
--- a/Server/data/configs/item_configs.json
+++ b/Server/data/configs/item_configs.json
@@ -139454,5 +139454,89 @@
"examine": "A chunk of rock.",
"name": "Rock",
"id": "1480"
+ },
+ {
+ "destroy": "true",
+ "turn90cw_anim": "1207",
+ "examine": "A staff with a spooky raven head attached.",
+ "walk_anim": "1205",
+ "low_alchemy": "80",
+ "turn90ccw_anim": "1208",
+ "attack_speed": "5",
+ "turn180_anim": "1206",
+ "defence_anim": "420",
+ "equipment_slot": "3",
+ "attack_anims": "419,419,419,419",
+ "stand_anim": "813",
+ "tradeable": "false",
+ "run_anim": "1210",
+ "archery_ticket_price": "0",
+ "id": "14654",
+ "stand_turn_anim": "1209",
+ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
+ "shop_price": "200",
+ "durability": null,
+ "high_alchemy": "120",
+ "weight": "2.2",
+ "weapon_interface": "1",
+ "render_anim": "28",
+ "attack_audios": "2555,0,0,0",
+ "name": "Staff of the raven"
+ },
+ {
+ "destroy": "true",
+ "turn90cw_anim": "1207",
+ "examine": "A staff with a spooky raven head attached.",
+ "walk_anim": "1205",
+ "low_alchemy": "80",
+ "turn90ccw_anim": "1208",
+ "attack_speed": "5",
+ "turn180_anim": "1206",
+ "defence_anim": "420",
+ "equipment_slot": "3",
+ "attack_anims": "419,419,419,419",
+ "stand_anim": "813",
+ "tradeable": "false",
+ "run_anim": "1210",
+ "archery_ticket_price": "0",
+ "id": "14655",
+ "stand_turn_anim": "1209",
+ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
+ "shop_price": "200",
+ "durability": null,
+ "high_alchemy": "120",
+ "weight": "2.2",
+ "weapon_interface": "1",
+ "render_anim": "28",
+ "attack_audios": "2555,0,0,0",
+ "name": "Staff of the raven"
+ },
+ {
+ "destroy": "true",
+ "turn90cw_anim": "1207",
+ "examine": "A staff with a spooky raven head attached.",
+ "walk_anim": "1205",
+ "low_alchemy": "80",
+ "turn90ccw_anim": "1208",
+ "attack_speed": "5",
+ "turn180_anim": "1206",
+ "defence_anim": "420",
+ "equipment_slot": "3",
+ "attack_anims": "419,419,419,419",
+ "stand_anim": "813",
+ "tradeable": "false",
+ "run_anim": "1210",
+ "archery_ticket_price": "0",
+ "id": "14656",
+ "stand_turn_anim": "1209",
+ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
+ "shop_price": "200",
+ "durability": null,
+ "high_alchemy": "120",
+ "weight": "2.2",
+ "weapon_interface": "1",
+ "render_anim": "28",
+ "attack_audios": "2555,0,0,0",
+ "name": "Staff of the raven"
}
]
\ No newline at end of file
diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json
index d2cd7b85b..3e5382a83 100644
--- a/Server/data/configs/npc_spawns.json
+++ b/Server/data/configs/npc_spawns.json
@@ -2015,6 +2015,10 @@
"npc_id": "782",
"loc_data": "{3150,3406,0,0,0}"
},
+ {
+ "npc_id": "6390",
+ "loc_data": "{3094,3258,0,0,0}"
+ },
{
"npc_id": "783",
"loc_data": "{3221,3435,0,1,0}"
diff --git a/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java b/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java
index db4d87062..17325e390 100644
--- a/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java
+++ b/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java
@@ -1,5 +1,6 @@
package core.game.interaction.item.toys;
+import api.ContentAPI;
import core.game.component.Component;
import core.game.component.ComponentDefinition;
import core.game.component.ComponentPlugin;
@@ -24,7 +25,7 @@ import java.util.Objects;
public class DiangoReclaimInterface extends ComponentPlugin {
private static final int COMPONENT_ID = 468;
public static final List- ITEMS = new ArrayList<>(20);
- public static final Item[] HOLIDAY_ITEMS = {YoyoPlugin.YOYO, ReindeerHatPlugin.ReindeerHat, BasketofEggsEvent.RUBBER_CHICKEN,ZombieHeadPlugin.ZOMBIE_HEAD, new Item(6857), new Item(6856), new Item(6858), new Item(6859), new Item(6860), new Item(6861), new Item(6862), new Item(6863), new Item(9920), new Item(9921),new Item(9922), new Item(9923), new Item(9924), new Item(9925), new Item(11019), new Item(11020), new Item(11021), new Item(11022), new Item(11789), new Item(11949), new Item(12634), new Item(14076), new Item(14077), new Item(14081),new Item(14595), new Item(14602), new Item(14603), new Item(14605)};
+ public static final Item[] HOLIDAY_ITEMS = {YoyoPlugin.YOYO, ReindeerHatPlugin.ReindeerHat, BasketofEggsEvent.RUBBER_CHICKEN,ZombieHeadPlugin.ZOMBIE_HEAD, new Item(6857), new Item(6856), new Item(6858), new Item(6859), new Item(6860), new Item(6861), new Item(6862), new Item(6863), new Item(9920), new Item(9921),new Item(9922), new Item(9923), new Item(9924), new Item(9925), new Item(11019), new Item(11020), new Item(11021), new Item(11022), new Item(11789), new Item(11949), new Item(12634), new Item(14076), new Item(14077), new Item(14081),new Item(14595), new Item(14602), new Item(14603), new Item(14605), new Item(14654)};
//initialize the plugin, add lists of items to the ITEMS list...
@Override
@@ -43,7 +44,12 @@ public class DiangoReclaimInterface extends ComponentPlugin {
}
//filter out items the player already has in their bank, inventory, or equipped
- Item[] reclaimables = ITEMS.stream().filter(Objects::nonNull).filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item)).toArray(Item[]::new);
+ Item[] reclaimables = ITEMS.stream().filter(Objects::nonNull)
+ .filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item)
+ && (item.getId() != 14654
+ || (!(ContentAPI.inInventory(player, 14655, 1) || ContentAPI.inEquipment(player, 14656, 1)) && player.getAttribute("sotr:purchased",false))
+ ))
+ .toArray(Item[]::new);
//only send items if there are some to send
if(reclaimables.length > 0) {
diff --git a/Server/src/main/java/core/net/packet/in/InteractionPacket.java b/Server/src/main/java/core/net/packet/in/InteractionPacket.java
index 0b581e843..0829de13a 100644
--- a/Server/src/main/java/core/net/packet/in/InteractionPacket.java
+++ b/Server/src/main/java/core/net/packet/in/InteractionPacket.java
@@ -26,7 +26,10 @@ import rs09.game.interaction.InteractionListener;
import rs09.game.interaction.InteractionListeners;
import rs09.game.world.repository.Repository;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.function.IntPredicate;
/**
* Handles the incoming interaction packets.
@@ -34,6 +37,8 @@ import java.util.List;
*/
public final class InteractionPacket implements IncomingPacket {
+ static int[] hweenNPCs = new int[] {307, 375, 743, 744, 755, 2634, 2690, 2691, 2692, 530, 531, 556, 557, 558, 559, 583, 585, 1860, 3299, 3671, 922, 970};
+
@Override
public void decode(Player player, int opcode, IoBuffer buffer) {
if (player == null) {
@@ -176,8 +181,13 @@ public final class InteractionPacket implements IncomingPacket {
return;
}
NPC shown = npc.getShownNPC(player);
- final Option option = shown.getInteraction().get(optionIndex);
+ Option option = shown.getInteraction().get(optionIndex);
if (option == null) {
+ if (Arrays.stream(hweenNPCs).anyMatch(i -> i == shown.getId())) {
+ option = new Option("trick-or-treat", -1);
+ }
+ }
+ if(option == null) {
PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player));
Interaction.handleInvalidInteraction(player, npc, Option.NULL);
return;
diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt
index 546addf50..3913ca055 100644
--- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt
+++ b/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt
@@ -4,6 +4,7 @@ import org.rs09.consts.Items
import rs09.game.content.ame.events.MysteriousOldManNPC
import rs09.game.content.ame.events.certer.CerterNPC
import rs09.game.content.ame.events.drilldemon.SeargentDamienNPC
+import rs09.game.content.ame.events.evilchicken.EvilChickenNPC
import rs09.game.content.ame.events.sandwichlady.SandwichLadyRENPC
import rs09.game.content.global.WeightBasedTable
import rs09.game.content.global.WeightedItem
@@ -28,6 +29,7 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
WeightedItem(Items.LOOP_HALF_OF_A_KEY_987,1,1,1.0)
)),
DRILL_DEMON(SeargentDamienNPC()),
+ EVIL_CHICKEN(EvilChickenNPC()),
SURPRISE_EXAM(MysteriousOldManNPC(),"sexam");
var type: String = ""
diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/evilchicken/EvilChickenNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/evilchicken/EvilChickenNPC.kt
new file mode 100644
index 000000000..d18cdc3ae
--- /dev/null
+++ b/Server/src/main/kotlin/rs09/game/content/ame/events/evilchicken/EvilChickenNPC.kt
@@ -0,0 +1,36 @@
+package rs09.game.content.ame.events.evilchicken
+
+import core.game.node.entity.Entity
+import core.game.node.entity.npc.NPC
+import core.game.node.item.GroundItemManager
+import core.game.node.item.Item
+import core.tools.RandomFunction
+import org.rs09.consts.Items
+import rs09.game.content.ame.RandomEventNPC
+import rs09.game.content.global.WeightBasedTable
+
+val ids = 2463..2468
+
+class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(2463) {
+ override fun talkTo(npc: NPC) {}
+
+ override fun init() {
+ super.init()
+ val index = (player.properties.combatLevel / 20) - 1
+ val id = ids.toList()[index]
+
+ this.transform(id)
+ this.attack(player)
+ this.isRespawn = false
+ }
+
+ override fun finalizeDeath(killer: Entity?) {
+ super.finalizeDeath(killer)
+ GroundItemManager.create(Item(Items.FEATHER_314, RandomFunction.random(50,300)), this.dropLocation, player)
+ }
+
+ override fun tick() {
+ super.tick()
+ if(!player.viewport.currentPlane.npcs.contains(this)) this.clear()
+ }
+}
\ No newline at end of file
diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt
index 01d2cc69a..85a718be2 100644
--- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt
+++ b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt
@@ -1,11 +1,19 @@
package rs09.game.content.global.worldevents.holiday.halloween
+import api.ContentAPI
import core.game.content.dialogue.DialoguePlugin
import core.game.content.dialogue.FacialExpression
import core.game.node.entity.combat.ImpactHandler
import core.game.node.entity.player.Player
+import core.game.node.entity.player.link.emote.Emotes
import core.game.node.item.Item
+import rs09.ServerStore
+import rs09.ServerStore.getInt
+import rs09.tools.END_DIALOGUE
+/**
+ * Handles grim's dialogue for the 2021 halloween event.
+ */
class GrimDialogue(player: Player? = null) : DialoguePlugin(player){
var firstSpeak = true
val candy = Item(14084)
@@ -14,10 +22,11 @@ class GrimDialogue(player: Player? = null) : DialoguePlugin(player){
}
override fun open(vararg args: Any?): Boolean {
- firstSpeak = !player.getAttribute("hween:grim_spoken",false)
+ firstSpeak = !player.getAttribute("hween2:grim_spoken",false)
if(firstSpeak){
npc("YOU! Yes.... you! Come here!")
+ player.musicPlayer.unlock(571)
stage = 0
} else {
npc("Hello, again, adventurer...")
@@ -34,18 +43,20 @@ class GrimDialogue(player: Player? = null) : DialoguePlugin(player){
3 -> player(FacialExpression.THINKING,"Candy...? You want me to bring","you... candy?").also { stage++ }
4 -> npc("Yes, candy! Did I not speak clearly","enough?").also { stage++ }
5 -> player(FacialExpression.ASKING,"Well how do I even get candy?").also { stage++ }
- 6 -> npc("It seems my candy collection has been scattered","into everything in 2009Scape!").also { stage++ }
- 7 -> npc("I broke open a rock earlier when I was moving","my chair here, and I found one!").also { stage++ }
- 8 -> npc("I suspect some of the vile creatures of","2009Scape have gotten ahold of some as well.").also { stage++ }
- 9 -> npc("I need YOU to go collect this for me.").also { stage++ }
- 10 -> player(FacialExpression.THINKING,"And what will I get in exchange?").also { stage++ }
- 11 -> npc("Well I won't KILL YOU for starters.").also { stage++ }
- 12 -> player(FacialExpression.ANGRY_WITH_SMILE, "Is that it?!").also { stage++ }
- 13 -> npc("Well, I guess I could also give you this","odd currency. I suspect one of these mortal","shops allows you to buy holiday items with it.").also { stage++ }
- 14 -> player(FacialExpression.AMAZED, "YOU MEAN CREDITS?!").also { stage++ }
- 15 -> npc("Yes, I suppose I do.").also { stage++ }
- 16 -> npc("I will give you 2 credits for every candy","you bring me.").also { stage++ }
- 17 -> npc("NOW GET TO WORK!").also { player.setAttribute("/save:hween:grim_spoken",true); stage = 1000 }
+ 6 -> npcl(FacialExpression.NEUTRAL,"It's Hallowe'en, you fool. Everyone's giving out candy.").also { stage++ }
+ 7 -> npc("You'll need to go and ask them for it each day.").also { stage++ }
+ 8 -> npc("You'll also need to be careful...","Something vile is on the prowl.").also { stage++ }
+ 9 -> player(FacialExpression.THINKING,"And what will I get in exchange?").also { stage++ }
+ 10 -> npc("Well I won't KILL YOU for starters.").also { stage++ }
+ 11 -> player(FacialExpression.ANGRY_WITH_SMILE, "Is that it?!").also { stage++ }
+ 12 -> npc("I've prepared a few... rewards, as well.").also { stage++ }
+ 13 -> player(FacialExpression.AMAZED, "YOU MEAN CREDITS?!").also { stage++ }
+ 14 -> npc("No, we're not doing that again.").also { stage++ }
+ 15 -> player(FacialExpression.SAD,"Oh.").also { stage++ }
+ 16 -> player(FacialExpression.NEUTRAL, "Ok, so what kind of rewards?").also { stage++ }
+ 17 -> npcl(FacialExpression.NEUTRAL, "I've fashioned a special-made staff to give out for this year's event.").also { stage++ }
+ 18 -> npcl(FacialExpression.NEUTRAL, "It is a decorative symbol of your engagement in this activity. Bring me candies, and it can be yours.").also { player.setAttribute("/save:hween2:grim_spoken",true); stage++ }
+ 19 -> npc("NOW GET TO WORK!").also { player.setAttribute("/save:hween2:grim_spoken",true); stage = 1000 }
100 -> npc("I do hope you have... candy for me?").also { stage++ }
@@ -54,15 +65,17 @@ class GrimDialogue(player: Player? = null) : DialoguePlugin(player){
} else {
player(FacialExpression.SAD, "No, I don't.").also { stage++ }
}
- 102 -> npc("THEN GET TO WORK!").also { player.impactHandler.manualHit(player,5,ImpactHandler.HitsplatType.DISEASE); stage++ }
- 103 -> player("YES SIR!").also { stage = 1000 }
+ 102 -> npc("A shame, indeed. Your candy totals right now are ${getCandyTotals(player)}.").also { stage++ }
+ 103 -> player(FacialExpression.FRIENDLY, "I'd like to see the reward shop, please.").also { stage++ }
+ 104, 105 -> handleRewardShop(player, buttonId)
150 -> {
val candies = player.inventory.getAmount(candy)
player.inventory.remove(Item(candy.id,candies))
- player.details.credits += candies * 2
- npc("Thank you, adventurer, I have awarded you","with ${candies * 2} 'credits.'").also { stage = 1000 }
+ addToCandyTotal(player, candies)
+ npcl(FacialExpression.NEUTRAL, "Excellent, you now have ${getCandyTotals(player)} candies.")
+ stage = 103
}
@@ -71,6 +84,143 @@ class GrimDialogue(player: Player? = null) : DialoguePlugin(player){
return true
}
+ fun handleRewardShop(player: Player, buttonId: Int){
+ val title = "You have ${getCandyTotals(player)} candies."
+ val hasUnlocked = player.getAttribute("sotr:purchased",false)
+ val hasRecolor1 = player.getAttribute("sotr:recolor1", false)
+ val hasRecolor2 = player.getAttribute("sotr:recolor2", false)
+ val hasEmote = player.emoteManager.isUnlocked(Emotes.TRICK)
+
+ if(!hasUnlocked && !hasEmote){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff of the Raven (40c)", "Trick Emote (10c)").also { stage++ }
+ 105 -> when(buttonId) {
+ 1 -> if (canPurchase(player, 40)) buyStaff(player) else npc(
+ FacialExpression.NEUTRAL,
+ "You can't afford that."
+ )
+ 2 -> if (canPurchase(player, 10)) buyEmote(player) else npc(
+ FacialExpression.NEUTRAL,
+ "You can't afford that"
+ )
+ }
+ }
+ }
+ else if(hasUnlocked && !hasEmote && !hasRecolor1 && !hasRecolor2){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Staff Orange Recolor (10c)", "Trick Emote (10c)").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 3 -> if(canPurchase(player, 10)) buyEmote(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ }
+ }
+ }
+ else if(hasUnlocked && !hasEmote && hasRecolor1 && !hasRecolor2){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Orange Recolor (10c)", "Trick Emote (10c)").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> if(canPurchase(player, 10)) buyEmote(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ }
+ }
+ }
+ else if(hasUnlocked && !hasEmote && !hasRecolor1 && hasRecolor2){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Trick Emote (10c)").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> if(canPurchase(player, 10)) buyEmote(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ }
+ }
+ }
+ else if(!hasUnlocked && hasEmote){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff of the Raven (40c)", "").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 40)) buyStaff(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> npc(FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 }
+ }
+ }
+ }
+ else if(hasUnlocked && hasEmote && !hasRecolor1 && !hasRecolor2){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Staff Orange Recolor (10c)").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ }
+ }
+ }
+ else if(hasUnlocked && hasEmote && !hasRecolor1 && hasRecolor2){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> npc(FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 }
+ }
+ }
+ }
+ else if(hasUnlocked && hasEmote && hasRecolor1 && !hasRecolor2){
+ when(stage){
+ 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Orange Recolor (10c)", "").also { stage++ }
+ 105 -> when(buttonId){
+ 1 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.")
+ 2 -> npc(FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 }
+ }
+ }
+ }
+ else {
+ npcl(FacialExpression.NEUTRAL, "You've already bought everything.")
+ stage = END_DIALOGUE
+ }
+ }
+
+ fun buyStaff(player: Player){
+ player.setAttribute("/save:sotr:purchased", true)
+ removeCandies(player, 40)
+ ContentAPI.addItem(player, 14654, 1)
+ stage = 104
+ handleRewardShop(player, -1)
+ }
+
+ fun buyRecolor1(player: Player){
+ player.setAttribute("/save:sotr:recolor1", true)
+ removeCandies(player, 10)
+ stage = 104
+ handleRewardShop(player, -1)
+ }
+
+ fun buyRecolor2(player: Player){
+ player.setAttribute("/save:sotr:recolor2", true)
+ removeCandies(player, 10)
+ stage = 104
+ handleRewardShop(player, -1)
+ }
+
+ fun buyEmote(player: Player){
+ player.emoteManager.unlock(Emotes.TRICK)
+ removeCandies(player, 10)
+ stage = 104
+ handleRewardShop(player, -1)
+ }
+
+ fun canPurchase(player: Player, cost: Int) : Boolean {
+ return getCandyTotals(player) >= cost
+ }
+
+ fun removeCandies(player: Player, amount: Int){
+ addToCandyTotal(player, -amount)
+ }
+
+ fun getCandyTotals(player: Player): Int {
+ return ServerStore.getArchive("hween2021-candies").getInt(player.username.toLowerCase())
+ }
+
+ fun addToCandyTotal(player: Player, amount: Int){
+ ServerStore.getArchive("hween2021-candies").put(player.username.toLowerCase(), getCandyTotals(player) + amount)
+ }
+
override fun getIds(): IntArray {
return intArrayOf(6390)
}
diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt
index cbc719bb9..03586de43 100644
--- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt
+++ b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt
@@ -9,22 +9,16 @@ import java.util.*
class SimpleHalloweenEvent : WorldEvent("hween"){
override fun checkActive(): Boolean {
- return false
+ return true
}
override fun initialize() {
plugins = PluginSet(
- CandyRewardPlugin(),
GrimDialogue()
)
- val grim = NPC(6390, Location(3247,3198))
- grim.isWalks = false
- grim.walkRadius = 0
- grim.isNeverWalks = true
- grim.init()
super.initialize()
GameWorld.settings?.message_model = 800
- GameWorld.settings?.message_string = "A mysterious figure has appeared in lumbridge cemetery! You should go investigate!"
+ GameWorld.settings?.message_string = "A mysterious figure has appeared in Draynor! You should go investigate!"
log("Initialized.")
}
}
\ No newline at end of file
diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/TrickOrTreatHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/TrickOrTreatHandler.kt
new file mode 100644
index 000000000..db1c404fe
--- /dev/null
+++ b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/TrickOrTreatHandler.kt
@@ -0,0 +1,95 @@
+package rs09.game.content.global.worldevents.holiday.halloween
+
+import api.ContentAPI
+import core.game.component.Component
+import core.game.content.dialogue.FacialExpression
+import core.game.node.entity.npc.NPC
+import core.game.node.entity.player.Player
+import core.game.system.task.Pulse
+import core.game.world.map.Location
+import core.game.world.update.flag.context.Animation
+import core.game.world.update.flag.context.Graphics
+import core.tools.RandomFunction
+import org.rs09.consts.Components
+import rs09.ServerStore
+import rs09.ServerStore.getInt
+import rs09.ServerStore.getString
+import rs09.game.content.dialogue.DialogueFile
+import rs09.game.interaction.InteractionListener
+import rs09.game.world.GameWorld
+import rs09.tools.END_DIALOGUE
+
+class TrickOrTreatHandler : InteractionListener() {
+ override fun defineListeners() {
+ on(NPC, "trick-or-treat"){player, node ->
+ val hasDone5 = getDailyTrickOrTreats(player) == 5
+ val hasDoneMe = getTrickOrTreatedNPCs(player).contains(node.name.toLowerCase())
+
+ if(hasDone5){
+ ContentAPI.sendNPCDialogue(player, node.id, "My informants tell me you've already collected candy from 5 people today.", FacialExpression.FRIENDLY)
+ return@on true
+ }
+
+ if(hasDoneMe){
+ ContentAPI.sendNPCDialogue(player, node.id, "You've already asked me today! Don't get greedy, now.", FacialExpression.ANNOYED)
+ return@on true
+ }
+
+ player.dialogueInterpreter.open(object : DialogueFile(){
+ override fun handle(componentID: Int, buttonID: Int) {
+ when(stage){
+ 0 -> playerl(FacialExpression.FRIENDLY, "Trick or treat!").also { if(RandomFunction.roll(20)) stage = 10 else stage++ }
+ 1 -> npcl(FacialExpression.FRIENDLY, "Very well, then, here you are my friend.").also { stage++ }
+ 2 -> {
+ player.dialogueInterpreter.sendItemMessage(14084, "They hand you a nicely-wrapped candy.")
+ ContentAPI.addItemOrDrop(player, 14084, 1)
+ registerNpc(player, npc!!)
+ incrementDailyToT(player)
+ stage = END_DIALOGUE
+ }
+
+ 10 -> npcl(FacialExpression.EVIL_LAUGH, "I CHOOSE TRICK!").also { player.lock(); GameWorld.submit(object : Pulse() {
+ var counter = 0
+ override fun pulse(): Boolean {
+ //gfx 1898
+ when(counter++){
+ 0 -> npc!!.visualize(Animation(1979), Graphics(1898)).also { npc!!.faceLocation(player.location) }
+ 2 -> player.dialogueInterpreter.close()
+ 5 -> player.interfaceManager.open(Component(Components.FADE_TO_BLACK_120))
+ 8 -> player.properties.teleportLocation = Location.create(3106, 3382, 0)
+ 12 -> {
+ player.interfaceManager.close()
+ player.interfaceManager.open(Component(Components.FADE_FROM_BLACK_170))
+ registerNpc(player, npc!!)
+ }
+ 15 -> player.interfaceManager.close().also { player.unlock() }
+ 16 -> return true
+ }
+ return false
+ }
+ }) }
+ }
+ }
+ }, node.asNpc())
+ return@on true
+ }
+ }
+
+ fun incrementDailyToT(player: Player){
+ ServerStore.getArchive("daily-tot-total")[player.username.toLowerCase()] = getDailyTrickOrTreats(player) + 1
+ }
+
+ fun getDailyTrickOrTreats(player: Player) : Int {
+ return ServerStore.getArchive("daily-tot-total").getInt(player.username.toLowerCase())
+ }
+
+ fun getTrickOrTreatedNPCs(player: Player): String {
+ return ServerStore.getArchive("daily-tot-npcs").getString(player.username.toLowerCase())
+ }
+
+ fun registerNpc(player: Player, npc: NPC){
+ var soFar = getTrickOrTreatedNPCs(player)
+ soFar += ":" + npc.name.toLowerCase() + ":"
+ ServerStore.getArchive("daily-tot-npcs")[player.username.toLowerCase()] = soFar
+ }
+}
\ No newline at end of file
diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/StaffOfTheRaven.kt b/Server/src/main/kotlin/rs09/game/interaction/item/StaffOfTheRaven.kt
new file mode 100644
index 000000000..f2364cb20
--- /dev/null
+++ b/Server/src/main/kotlin/rs09/game/interaction/item/StaffOfTheRaven.kt
@@ -0,0 +1,70 @@
+package rs09.game.interaction.item
+
+import api.ContentAPI
+import core.game.node.entity.player.Player
+import core.game.node.item.Item
+import core.game.world.update.flag.context.Graphics
+import rs09.game.content.dialogue.DialogueFile
+import rs09.game.interaction.InteractionListener
+import rs09.tools.END_DIALOGUE
+
+/**
+ * Handles the Staff of the Raven's (2021 Hween Reward) Recolor Transformation
+ */
+class StaffOfTheRaven : InteractionListener() {
+ val ids = intArrayOf(14654, 14655, 14656)
+ override fun defineListeners() {
+ on(ids, ITEM, "recolor", "operate"){player, node ->
+ val hasUnlocked = player.getAttribute("sotr:purchased",false)
+ val hasRecolorA = player.getAttribute("sotr:recolor1", false)
+ val hasRecolorB = player.getAttribute("sotr:recolor2", false)
+ val isBase = node.id == 14654
+ val isOperate = ContentAPI.getUsedOption(player) == "operate"
+
+ if(!hasUnlocked){
+ //Remove the item if the player has not purchased it. Just in case.
+ switchStaff(player, null, isOperate, node.asItem())
+ return@on true
+ }
+
+ if(!isBase){
+ switchStaff(player, 14654, isOperate, node.asItem())
+ return@on true
+ }
+
+ player.dialogueInterpreter.open(object : DialogueFile(){
+ override fun handle(componentID: Int, buttonID: Int) {
+ if(!hasRecolorA && !hasRecolorB){
+ dialogue("You do not have any recolors unlocked.")
+ stage = END_DIALOGUE
+ } else if(hasRecolorA && !hasRecolorB){
+ switchStaff(player, 14655, isOperate, node.asItem())
+ } else if(hasRecolorB && !hasRecolorA){
+ switchStaff(player, 14656, isOperate, node.asItem())
+ } else {
+ when (stage) {
+ 0 -> options("Purple", "Orange").also { stage++ }
+ 1 -> when (buttonID) {
+ 1 -> switchStaff(player, 14655, isOperate, node.asItem())
+ 2 -> switchStaff(player, 14656, isOperate, node.asItem())
+ else -> { }
+ }.also { end() }
+ }
+ }
+ }
+ })
+ return@on true
+ }
+ }
+
+ fun switchStaff(player: Player, to: Int?, equipped: Boolean, original: Item){
+ player.graphics(Graphics(1119))
+ val item: Item? = if(to != null) Item(to) else to
+
+ if(equipped){
+ player.equipment.replace(item, original.slot)
+ } else {
+ player.inventory.replace(item, original.slot)
+ }
+ }
+}
\ No newline at end of file
diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt
index c7b1ea3d4..93e1b2e4a 100644
--- a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt
+++ b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt
@@ -424,7 +424,7 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){
}
define("testlady",Command.Privilege.ADMIN){player,_ ->
- player.antiMacroHandler.event = RandomEvents.SURPRISE_EXAM.npc.create(player,null,"sexam")
+ player.antiMacroHandler.event = RandomEvents.EVIL_CHICKEN.npc.create(player,null,"sexam")
player.antiMacroHandler.event!!.init()
}