diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index 3d2b57d6c..8c7fc648c 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -10550,6 +10550,34 @@ { "npc_id": "2790", "loc_data": "{3163,4822,0,0,6}" + }, + { + "npc_id": "6117", + "loc_data": "{1884,5020,0,0,6}" + }, + { + "npc_id": "6134", + "loc_data": "{1890,5018,0,0,0}" + }, + { + "npc_id": "6132", + "loc_data": "{1883,5029,0,0,6}" + }, + { + "npc_id": "6133", + "loc_data": "{1883,5026,0,0,6}" + }, + { + "npc_id": "6131", + "loc_data": "{1885,5026,0,0,6}" + }, + { + "npc_id": "490", + "loc_data": "{1887,5026,0,0,6}" + }, + { + "npc_id": "487", + "loc_data": "{1885,5029,0,0,6}" } ] diff --git a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt index 2239c260c..5e6e6ee0c 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt @@ -24,6 +24,8 @@ import core.game.world.map.path.Pathfinder import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import core.tools.RandomFunction +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import org.rs09.consts.Items import rs09.game.ai.AIRepository import rs09.game.ge.GrandExchangeOffer @@ -324,7 +326,7 @@ class ScriptAPI(private val bot: Player) { fun randomWalkTo(loc: Location, radius: Int) { if(!bot.walkingQueue.isMoving) { - Executors.newSingleThreadExecutor().execute { + GlobalScope.launch { var newloc = loc.transform(RandomFunction.random(radius,-radius), RandomFunction.random(radius,-radius), 0) walkToIterator(newloc) diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt index 904246173..2da325e26 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt +++ b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt @@ -16,7 +16,7 @@ class RandomEventManager(val player: Player) { fun fireEvent(){ val ame = RandomEvents.values().random() - event = ame.npc.create(player,ame.loot) + event = ame.npc.create(player,ame.loot,ame.type) event!!.init() nextSpawn = GameWorld.ticks + DELAY_TICKS SystemLogger.logRE("Fired ${event!!.name} for ${player.username}") diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt index cf34c2fed..f82d75100 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt +++ b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt @@ -9,6 +9,7 @@ import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.map.path.Pathfinder import core.game.world.update.flag.context.Graphics +import rs09.game.content.ame.events.MysteriousOldManNPC import rs09.game.content.global.WeightBasedTable import rs09.game.world.GameWorld.Pulser import rs09.tools.secondsToTicks @@ -23,8 +24,9 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { var initialized = false var ticksLeft = secondsToTicks(600) - open fun create(player: Player, loot: WeightBasedTable? = null): RandomEventNPC{ + open fun create(player: Player, loot: WeightBasedTable? = null, type: String = ""): RandomEventNPC{ val event = this::class.createInstance() + if(event is MysteriousOldManNPC) event.type = type event.loot = loot event.player = player event.spawnLocation = RegionManager.getSpawnLocation(player,this) @@ -53,7 +55,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { pulseManager.run(object : MovementPulse(this,player, Pathfinder.DUMB){ override fun pulse(): Boolean { face(player) - 7 return false } }) 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 eacb03927..06e004b5c 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt +++ b/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt @@ -1,6 +1,7 @@ package rs09.game.content.ame import org.rs09.consts.Items +import rs09.game.content.ame.events.MysteriousOldManNPC import rs09.game.content.ame.events.certer.CerterNPC import rs09.game.content.ame.events.drilldemon.SeargentDamienNPC import rs09.game.content.ame.events.sandwichlady.SandwichLadyRENPC @@ -26,7 +27,14 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985,1,1,1.0), WeightedItem(Items.LOOP_HALF_OF_A_KEY_987,1,1,1.0) )), - DRILL_DEMON(SeargentDamienNPC()); + DRILL_DEMON(SeargentDamienNPC()), + SURPRISE_EXAM(MysteriousOldManNPC(),"sexam"); + + var type: String = "" + + constructor(npc: RandomEventNPC, type: String) : this(npc,null){ + this.type = type + } companion object { @JvmField diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManDialogue.kt new file mode 100644 index 000000000..0518b4852 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManDialogue.kt @@ -0,0 +1,40 @@ +package rs09.game.content.ame.events + +import core.game.node.entity.player.Player +import rs09.game.content.ame.events.supriseexam.SurpriseExamUtils +import rs09.game.content.dialogue.DialogueFile + +class MysteriousOldManDialogue(val type: String) : DialogueFile() { + + val CHOICE_STAGE = 50000 + + override fun handle(componentID: Int, buttonID: Int) { + + if(type == "sexam" && stage < CHOICE_STAGE){ + npc("Would you like to come do a surprise exam?") + stage = CHOICE_STAGE + } + + else if(stage >= CHOICE_STAGE){ + when(stage) { + CHOICE_STAGE -> options("Yeah, sure!", "No, thanks.").also { stage++ } + CHOICE_STAGE.substage(1) -> when(buttonID){ + 1 -> { + end() + teleport(player!!,type) + player!!.antiMacroHandler.event?.terminate() + } + 2 -> { + end() + player!!.antiMacroHandler.event?.terminate() + } + } + } + } + } + fun teleport(player: Player,type: String){ + when(type){ + "sexam" -> SurpriseExamUtils.teleport(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManNPC.kt new file mode 100644 index 000000000..ce177eca3 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManNPC.kt @@ -0,0 +1,31 @@ +package rs09.game.content.ame.events + +import core.game.node.entity.npc.NPC +import core.tools.RandomFunction +import org.rs09.consts.NPCs +import rs09.game.content.ame.RandomEventNPC +import rs09.game.content.global.WeightBasedTable + +class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { + override fun init() { + super.init() + sayLine() + } + + override fun tick() { + super.tick() + if(RandomFunction.random(1,10) == 5) sayLine() + } + + fun sayLine() { + when(type){ + "sexam" -> sendChat("Surprise exam, ${player.username.capitalize()}!") + } + } + + override fun talkTo(npc: NPC) { + when(type){ + "sexam" -> player.dialogueInterpreter.open(MysteriousOldManDialogue("sexam"),this.asNpc()) + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/MordautDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/MordautDialogue.kt new file mode 100644 index 000000000..278f2b4f4 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/MordautDialogue.kt @@ -0,0 +1,65 @@ +package rs09.game.content.ame.events.supriseexam + +import core.game.component.Component +import core.game.content.dialogue.FacialExpression +import rs09.game.content.dialogue.DialogueFile +import rs09.tools.END_DIALOGUE + +class MordautDialogue(val examComplete: Boolean, val questionCorrect: Boolean = false, val fromInterface: Boolean = false) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + if(examComplete){ + if(player!!.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) == -1){ + SurpriseExamUtils.pickRandomDoor(player!!) + } + val door = player!!.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) + val doortext = when(door){ + 2188 -> "red cross" + 2189 -> "blue star" + 2192 -> "purple circle" + 2193 -> "green square" + else -> "REEEEEEEEEEEEEEEE" + } + + npc("Please exit through the $doortext door.") + stage = END_DIALOGUE + } else if(fromInterface){ + + if(questionCorrect){ + if(player!!.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT,0) == 3){ + player!!.dialogueInterpreter.open(MordautDialogue(true),npc) + } else { + when(stage++){ + 0 -> npc("Excellent work!") + 1 -> npc("Now for another...") + 2 -> { + end() + player!!.interfaceManager.open(Component(SurpriseExamUtils.INTERFACE)) + } + } + } + } else { + when(stage++){ + 0 -> npc("I'm afraid that isn't correct.") + 1 -> npc("Now for another...") + 2 -> { + end() + player!!.interfaceManager.open(Component(SurpriseExamUtils.INTERFACE)) + } + } + } + + } else { + when(stage++){ + 0 -> npc("Please answer these questions for me.") + 1 -> { + end() + player!!.interfaceManager.open(Component(SurpriseExamUtils.INTERFACE)) + } + } + } + } + + override fun npc(vararg messages: String?): Component? { + return super.npc(FacialExpression.OLD_NORMAL,*messages) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEDoorDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEDoorDialogue.kt new file mode 100644 index 000000000..4f63efa48 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEDoorDialogue.kt @@ -0,0 +1,16 @@ +package rs09.game.content.ame.events.supriseexam + +import rs09.game.content.dialogue.DialogueFile +import rs09.tools.END_DIALOGUE + +class SEDoorDialogue(val preExam: Boolean) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + stage = if(preExam){ + dialogue("I should probably speak with Mr. Mordaut first.") + END_DIALOGUE + } else { + dialogue("The door won't budge. Perhaps I should","ask for directions.") + END_DIALOGUE + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEPatternInterface.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEPatternInterface.kt new file mode 100644 index 000000000..73ac97c4d --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEPatternInterface.kt @@ -0,0 +1,36 @@ +package rs09.game.content.ame.events.supriseexam + +import core.game.node.entity.npc.NPC +import org.rs09.consts.Components +import org.rs09.consts.NPCs +import rs09.game.interaction.InterfaceListener + +class SEPatternInterface : InterfaceListener() { + + val COMPONENT = Components.PATTERN_NEXT_103 + + override fun defineListeners() { + + on(COMPONENT){player, component, opcode, buttonID, slot, itemID -> + val index = buttonID - 10 + val correctIndex = player.getAttribute(SurpriseExamUtils.SE_KEY_INDEX,0) + player.interfaceManager.close() + + if(index == correctIndex){ + player.incrementAttribute(SurpriseExamUtils.SE_KEY_CORRECT) + val done = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT,0) == 3 + player.dialogueInterpreter.open(MordautDialogue(examComplete = done,questionCorrect = true,fromInterface = true), NPC(NPCs.MR_MORDAUT_6117)) + } else { + player.dialogueInterpreter.open(MordautDialogue(examComplete = false,questionCorrect = false,fromInterface = true),NPC(NPCs.MR_MORDAUT_6117)) + } + return@on true + } + + onOpen(COMPONENT){player, component -> + SurpriseExamUtils.generateInterface(player) + return@onOpen true + } + + } + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SupriseExamListeners.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SupriseExamListeners.kt new file mode 100644 index 000000000..94e621d0c --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SupriseExamListeners.kt @@ -0,0 +1,60 @@ +package rs09.game.content.ame.events.supriseexam + +import core.game.component.Component +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.game.interaction.InteractionListener +import rs09.game.interaction.inter.ExperienceInterface + +class SupriseExamListeners : InteractionListener() { + val MORDAUT = NPCs.MR_MORDAUT_6117 + val BOOK_OF_KNOWLEDGE = Items.BOOK_OF_KNOWLEDGE_11640 + override fun defineListeners() { + + on(MORDAUT,NPC,"talk-to"){player, node -> + player.faceLocation(Location.create(1886, 5024, 0)) + val examComplete = player.getAttribute(SurpriseExamUtils.SE_KEY_CORRECT,0) == 3 + player.dialogueInterpreter.open(MordautDialogue(examComplete),node.asNpc()) + return@on true + } + + on(SurpriseExamUtils.SE_DOORS,OBJECT,"open"){player, node -> + val correctDoor = player.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) + + if(correctDoor == -1){ + player.dialogueInterpreter.open(SEDoorDialogue(true)) + return@on true + } + + if(node.id == correctDoor){ + SurpriseExamUtils.cleanup(player) + return@on true + } + + player.dialogueInterpreter.open(SEDoorDialogue(false)) + return@on true + } + + on(BOOK_OF_KNOWLEDGE,ITEM,"read"){player, node -> + player.setAttribute("caller"){skill: Int,p: Player -> + if(p.inventory.remove(Item(BOOK_OF_KNOWLEDGE))) { + val level = p.skills.getStaticLevel(skill) + val experience = level * 15.0 + p.skills.addExperience(skill, experience) + } + } + player.interfaceManager.open(Component(ExperienceInterface.COMPONENT_ID)) + return@on true + } + + } + + override fun defineDestinationOverrides() { + setDest(NPC,MORDAUT){_ -> + return@setDest Location.create(1886, 5025, 0) + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt new file mode 100644 index 000000000..d634915de --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt @@ -0,0 +1,80 @@ +package rs09.game.content.ame.events.supriseexam + +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.rs09.consts.Components +import org.rs09.consts.Items + +object SurpriseExamUtils { + + val SE_KEY_LOC = "supexam:loc" + val SE_KEY_INDEX = "supexam:index" + val SE_LOGOUT_KEY = "suprise_exam" + val SE_DOOR_KEY = "supexam:door" + val INTER_PATTERN_CHILDS = intArrayOf(6,7,8) + val INTER_OPTION_CHILDS = intArrayOf(10,11,12,13) + val SE_DOORS = intArrayOf(2188,2189,2192,2193) + val INTERFACE = Components.PATTERN_NEXT_103 + val SE_KEY_CORRECT = "supexam:correct" + val sets = arrayOf( + intArrayOf(Items.GARDENING_TROWEL_5325,Items.SECATEURS_5329,Items.SEED_DIBBER_5343,Items.RAKE_5341), + intArrayOf(Items.SALMON_329,Items.SHARK_385,Items.TROUT_333,Items.SHRIMPS_315), + intArrayOf(Items.BRONZE_SWORD_1277,Items.WOODEN_SHIELD_1171,Items.BRONZE_MED_HELM_1139,Items.ADAMANT_BATTLEAXE_1371), + intArrayOf(Items.FLY_FISHING_ROD_309,Items.BARBARIAN_ROD_11323,Items.SMALL_FISHING_NET_303,Items.HARPOON_311) + ) + + fun teleport(player: Player){ + player.setAttribute(SE_KEY_LOC,player.location) + player.addLogoutListener(SE_LOGOUT_KEY){p -> + p.location = p.getAttribute(SE_KEY_LOC,null) + } + player.properties.teleportLocation = Location.create(1886, 5025, 0) + } + + fun cleanup(player: Player){ + player.properties.teleportLocation = player.getAttribute(SE_KEY_LOC,null) + player.removeLogoutListener(SE_LOGOUT_KEY) + player.removeAttribute(SE_KEY_LOC) + player.removeAttribute(SE_KEY_INDEX) + player.removeAttribute(SE_KEY_CORRECT) + player.pulseManager.run(object : Pulse(){ + override fun pulse(): Boolean { + val reward = Item(Items.BOOK_OF_KNOWLEDGE_11640) + if(!player.inventory.add(reward)){ + GroundItemManager.create(reward,player) + } + return true + } + }) + } + + fun generateInterface(player: Player){ + val set = sets.random() + val setIndex = sets.indexOf(set) + + val tempList = set.toList().shuffled().toMutableList() + val correctOpt = tempList.random() + val optIndex = tempList.indexOf(correctOpt) + + tempList.remove(correctOpt) + for(i in INTER_PATTERN_CHILDS.indices) player.packetDispatch.sendItemOnInterface(tempList[i],1, INTERFACE, INTER_PATTERN_CHILDS[i]) + for(i in INTER_OPTION_CHILDS.indices){ + if(i == optIndex) player.packetDispatch.sendItemOnInterface(correctOpt,1, INTERFACE, INTER_OPTION_CHILDS[i]) + else player.packetDispatch.sendItemOnInterface(getFalseSet(setIndex)[i],1, INTERFACE, INTER_OPTION_CHILDS[i]) + } + player.setAttribute(SE_KEY_INDEX,optIndex) + } + + fun getFalseSet(trueSetIndex: Int): IntArray{ + val tempSets = sets.toMutableList() + tempSets.removeAt(trueSetIndex) + return tempSets.random() + } + + fun pickRandomDoor(player: Player){ + player.setAttribute(SE_DOOR_KEY, SE_DOORS.random()) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt index 2489af478..992db1222 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt @@ -103,4 +103,8 @@ abstract class DialogueFile { return this + stage } + fun dialogue(vararg messages: String){ + player?.dialogueInterpreter?.sendDialogue(*messages) + } + } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt index faf2df926..015235db6 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt @@ -5,10 +5,10 @@ import core.game.component.ComponentDefinition import core.game.component.ComponentPlugin import core.game.node.entity.player.Player import core.game.node.entity.player.link.audio.Audio -import rs09.game.system.SystemLogger +import core.game.node.entity.skill.Skills import core.plugin.Initializable import core.plugin.Plugin -import core.game.node.entity.skill.Skills +import rs09.game.system.SystemLogger /** * Represents the experience interface. @@ -16,9 +16,10 @@ import core.game.node.entity.skill.Skills */ @Initializable class ExperienceInterface() : ComponentPlugin() { + @Throws(Throwable::class) override fun newInstance(arg: Any?): Plugin { - ComponentDefinition.put(134, this) + ComponentDefinition.put(COMPONENT_ID, this) return this } @@ -36,7 +37,9 @@ class ExperienceInterface() : ComponentPlugin() { } val caller = player.attributes["caller"] caller ?: return true - (caller as Plugin<*>).handleSelectionCallback(confirmedSkill, player) + if(caller is Plugin<*>) + caller.handleSelectionCallback(confirmedSkill, player) + else (caller as (Int,Player) -> Unit).invoke(confirmedSkill,player) player.audioManager.send(SOUND) player.interfaceManager.close() } @@ -91,5 +94,7 @@ class ExperienceInterface() : ComponentPlugin() { * Represents the sound to send. */ private val SOUND = Audio(1270, 12, 1) + @JvmField + public val COMPONENT_ID = 134 } } \ 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 2d824b214..224ca8a63 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 @@ -457,7 +457,7 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ } define("testlady",Command.Privilege.ADMIN){player,_ -> - player.antiMacroHandler.event = RandomEvents.DRILL_DEMON.npc.create(player,null) + player.antiMacroHandler.event = RandomEvents.SURPRISE_EXAM.npc.create(player,null,"sexam") player.antiMacroHandler.event!!.init() } }