Implemented a brand new cutscene system

Converted Lost Tribe's cutscene to the new system
This commit is contained in:
Ceikry
2022-03-27 05:14:16 +00:00
committed by Ryan
parent 776931c7a4
commit 7709c3bb04
10 changed files with 488 additions and 225 deletions
@@ -1,5 +1,6 @@
package core.game.node.entity.combat;
import api.events.SelfDeath;
import core.game.container.Container;
import core.game.container.ContainerType;
import core.game.node.Node;
@@ -84,6 +85,7 @@ public final class DeathTask extends NodeTask {
e.getImpactHandler().getImpactLog().clear();// check if this needs to be
// before finalize
e.getImpactHandler().setDisabledTicks(4);
e.dispatch(new SelfDeath(killer));
}
@Override
+356
View File
@@ -0,0 +1,356 @@
import api.*
import api.events.EventHook
import api.events.SelfDeath
import core.game.component.Component
import core.game.content.dialogue.FacialExpression
import core.game.interaction.MovementPulse
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.scenery.Scenery
import core.game.system.task.Pulse
import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.map.Region
import core.game.world.map.RegionManager
import core.game.world.map.build.DynamicRegion
import org.rs09.consts.Components
import rs09.ServerConstants
import rs09.game.Event
import rs09.game.camerautils.PlayerCamera
import rs09.game.system.SystemLogger
import rs09.game.world.GameWorld
/**
* A utility class for making cutscenes.
* @author Ceikry
*/
abstract class Cutscene(val player: Player) {
lateinit var region: Region
lateinit var base: Location
var exitLocation: Location = player.location.transform(0,0,0)
var ended = false
val camera = PlayerCamera(player)
private val addedNPCs = HashMap<Int, ArrayList<NPC>>()
abstract fun setup()
abstract fun runStage(stage: Int)
/**
* Creates a new dynamic copy of the region identified by regionId, sets this cutscene's region as this new copy, and clears any cutscene-spawned NPCs from the previous region.
* @param regionId the region ID to duplicate.
*/
fun loadRegion(regionId: Int)
{
clearNPCs()
logCutscene("Creating new instance of region $regionId for ${player.username}.")
region = DynamicRegion.create(regionId)
logCutscene("Dynamic region instantiated for ${player.username}. Global coordinates: ${region.baseLocation}.")
base = region.baseLocation
}
/**
* Fade the player's view to black. This process can be safely assumed to take about 8 ticks to complete.
*/
fun fadeToBlack()
{
logCutscene("Fading ${player.username}'s screen to black.")
player.interfaceManager.closeOverlay()
player.interfaceManager.openOverlay(Component(Components.FADE_TO_BLACK_120))
}
/**
* Fade the player's view from black back to normal. This process can be safely assumed to take about 6 ticks to complete.
*/
fun fadeFromBlack()
{
logCutscene("Fading ${player.username}'s screen from black to normal.")
player.interfaceManager.closeOverlay()
player.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170))
}
/**
* Teleports an entity to a pair of coordinates within the currently active cutscene region
* @param entity the entity to teleport
* @param regionX the region X coordinate to teleport the entity to (0-63)
* @param regionY the region Y coordinate to teleport the entity to (0-63)
* @param plane (optional) the plane to teleport to (0-3)
*/
fun teleport(entity: Entity, regionX: Int, regionY: Int, plane: Int = 0)
{
val newLoc = base.transform(regionX, regionY, plane)
logCutscene("Teleporting ${entity.username} to coordinates: LOCAL[$regionX,$regionY,$plane] GLOBAL[${newLoc.x},${newLoc.y},$plane].")
entity.properties.teleportLocation = newLoc
}
/**
* Moves an entity to the given coordinates within the currently active cutscene region
* @param entity the entity to move
* @param regionX the region X coordinate to move the entity to (0-63)
* @param regionY the region Y coordinate to move the entity to (0-63)
*/
fun move(entity: Entity, regionX: Int, regionY: Int)
{
logCutscene("Moving ${entity.username} to LOCAL[$regionX,$regionY].")
entity.pulseManager.run(object : MovementPulse(entity, base.transform(regionX,regionY,0)) {
override fun pulse(): Boolean {
return true
}
})
}
/**
* Sends a dialogue to the player using the given NPC ID, which updates the cutscene stage by default when continued.
* @param npcId the ID of the NPC to send a dialogue for
* @param expression the FacialExpression the NPC should use
* @param message the message to send
* @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default.
*/
fun dialogueUpdate(npcId: Int, expression: FacialExpression, message: String, onContinue: () -> Unit = {incrementStage()})
{
logCutscene("Sending NPC dialogue update.")
sendNPCDialogue(player, npcId, message, expression)
player.dialogueInterpreter.addAction { _,_ -> onContinue.invoke() }
}
/**
* Sends a non-NPC dialogue to the player, which updates the cutscene stage by default when continued
* @param message the message to send
* @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default.
*/
fun dialogueUpdate(message: String, onContinue: () -> Unit = {incrementStage()})
{
logCutscene("Sending standard dialogue update.")
sendDialogue(player, message)
player.dialogueInterpreter.addAction {_,_ -> onContinue.invoke()}
}
/**
* Sends a player dialogue, which updates the cutscene stage by default when continued
* @param expression the FacialExpression to use
* @param message the message to send
* @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default.
*/
fun playerDialogueUpdate(expression: FacialExpression, message: String, onContinue: () -> Unit = {incrementStage()})
{
logCutscene("Sending player dialogue update")
sendPlayerDialogue(player, message, expression)
player.dialogueInterpreter.addAction{_,_ -> onContinue.invoke()}
}
/**
* Updates the stage after a set number of ticks.
* @param ticks the number of ticks to wait
* @param newStage (optional) the new stage to update to. If not passed, stage is incremented instead.
*/
fun timedUpdate(ticks: Int, newStage: Int = -1)
{
logCutscene("Executing timed updated for $ticks ticks.")
GameWorld.Pulser.submit(object : Pulse(ticks)
{
override fun pulse(): Boolean {
if(newStage == -1)
incrementStage()
else
updateStage(newStage)
return true
}
})
}
/**
* Retrieves the first NPC from the added-to-cutscene NPCs with a matching ID.
* @param id the ID to grab for.
* @return the first NPC, or null if there are no matching NPCs.
*/
fun getNPC(id: Int): NPC?
{
return addedNPCs[id]?.firstOrNull()
}
/**
* Retrieves all NPCs from the added-to-cutscene NPCs with a matching ID.
* @param id the ID to grab for.
* @return an ArrayList of the matching NPCs, which will be empty if there were no matches.
*/
fun getNPCs(id: Int): ArrayList<NPC>
{
return addedNPCs[id] ?: ArrayList()
}
/**
* Retrieves the object in the region at the given regionX and regionY
* @param regionX the region-local X coordinate to grab the object from (0-63)
* @param regionY the region-local Y coordinate to grab the object from (0-63)
* @param plane the plane to grab the object from (0-3)
*/
fun getObject(regionX: Int, regionY: Int, plane: Int = 0): Scenery?
{
val obj = RegionManager.getObject(base.transform(regionX,regionY,plane))
logCutscene("Retrieving object at LOCAL[$regionX,$regionY], GOT: ${obj?.definition?.name ?: "null"}.")
return obj
}
/**
* Adds an NPC to the cutscene with the given ID, at the region-local X and Y coordinates.
* @param id the ID of the NPC to add.
* @param regionX the region-local X coordinate to place the NPC at
* @param regionY the region-local Y coordinate to place the NPC at
* @param direction the direction the NPC faces when it is initially placed.
* @param plane the plane to place te NPC on. Default is 0.
*/
fun addNPC(id: Int, regionX: Int, regionY: Int, direction: Direction, plane: Int = 0)
{
val npc = NPC(id)
npc.isRespawn = false
npc.isAggressive = false
npc.isWalks = false
npc.location = base.transform(regionX, regionY, plane)
npc.init()
npc.faceLocation(npc.location.transform(direction))
val npcs = addedNPCs[id] ?: ArrayList()
npcs.add(npc)
addedNPCs[id] = npcs
logCutscene("Added NPC $id at location LOCAL[$regionX,$regionY,$plane] GLOBAL[${npc.location.x},${npc.location.y},$plane]")
}
fun start()
{
logCutscene("Starting cutscene for ${player.username}.")
region = RegionManager.forId(player.location.regionId)
base = RegionManager.forId(player.location.regionId).baseLocation
setup()
runStage(player.getCutsceneStage())
setAttribute(player, ATTRIBUTE_CUTSCENE, this)
setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, 0)
player.interfaceManager.removeTabs(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)
player.properties.isSafeZone = true
player.properties.safeRespawn = player.location
player.lock()
player.hook(Event.SelfDeath, CUTSCENE_DEATH_HOOK)
player.logoutListeners["cutscene"] = {player -> player.location = exitLocation; player.getCutscene()?.end() }
player.antiMacroHandler.enabled = false
}
/**
* Ends this cutscene, fading the screen to black, teleporting the player to the exit location, and then fading it back in and executing the endActions passed to this method.
* @param endActions (optional) a method that executes when the cutscene fully completes
*/
fun end(endActions: (() -> Unit)? = null)
{
ended = true
fadeToBlack()
GameWorld.Pulser.submit(object : Pulse(){
var tick: Int = 0
override fun pulse(): Boolean {
when(tick++)
{
8 -> player.properties.teleportLocation = exitLocation
9 -> fadeFromBlack()
16 -> {
endActions?.invoke()
player.removeAttribute(ATTRIBUTE_CUTSCENE)
player.removeAttribute(ATTRIBUTE_CUTSCENE_STAGE)
player.properties.isSafeZone = false
player.properties.safeRespawn = ServerConstants.HOME_LOCATION
player.interfaceManager.restoreTabs()
player.unlock()
clearNPCs()
player.unhook(CUTSCENE_DEATH_HOOK)
player.logoutListeners.remove("cutscene")
player.antiMacroHandler.enabled = true
return true
}
}
return false
}
})
}
/**
* Moves the camera to the specified regionX and regionY
* @param regionX the region-local X coordinate to move the camera to (0-63)
* @param regionY the region-local Y coordinate to move the camera to (0-63)
* @param height (optional) the height of the camera, defaults to 300.
* @param speed (optional) the speed of the camera transition, defaults to 100.
*/
fun moveCamera(regionX: Int, regionY: Int, height: Int = 300, speed: Int = 100)
{
val globalLoc = base.transform(regionX, regionY, 0)
camera.panTo(globalLoc.x, globalLoc.y, height, speed)
}
/**
* Rotates the camera to face the given region-local X and Y coordinates
* @param regionX the region-local X coordinate to rotate the camera to (0-63)
* @param regionY the region-local Y coordinate to rotate the camera to (0-63)
* @param height (optional) the height of the camera, defaults to 300.
* @param speed (optional) the speed of the camera transition, defaults to 100.
*/
fun rotateCamera(regionX: Int, regionY: Int, height: Int = 300, speed: Int = 100)
{
val globalLoc = base.transform(regionX, regionY, 0)
camera.rotateTo(globalLoc.x, globalLoc.y, height, speed)
}
/**
* Sets the location the player is placed at when the cutscene ends.
*/
fun setExit(location: Location)
{
exitLocation = location
}
private fun loadCurrentStage()
{
if(ended) return
runStage(player.getCutsceneStage())
}
/**
* Increments the player's stage attribute by 1
*/
fun incrementStage()
{
setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, player.getCutsceneStage() + 1)
loadCurrentStage()
}
/**
* Sets the player's stage attribute to a new stage
*/
fun updateStage(newStage: Int)
{
setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, newStage)
loadCurrentStage()
}
fun logCutscene(message: String)
{
if(ServerConstants.LOG_CUTSCENE)
SystemLogger.logInfo("[CUTSCENE: ${this.javaClass.simpleName}] $message")
}
fun clearNPCs() {
for(entry in addedNPCs.entries)
{
logCutscene("Clearing ${entry.value.size} NPCs with ID ${entry.key} for ${player.username}.")
for(npc in entry.value) npc.clear()
}
addedNPCs.clear()
}
companion object {
const val ATTRIBUTE_CUTSCENE = "cutscene"
const val ATTRIBUTE_CUTSCENE_STAGE = "cutscene:stage"
object CUTSCENE_DEATH_HOOK : EventHook<SelfDeath>
{
override fun process(entity: Entity, event: SelfDeath) {
if(entity !is Player) return
entity.getCutscene()?.end() ?: entity.unhook(this)
}
}
}
}
+9
View File
@@ -1,5 +1,6 @@
package api
import Cutscene
import core.cache.def.impl.ItemDefinition
import core.cache.def.impl.SceneryDefinition
import core.game.component.Component
@@ -1439,3 +1440,11 @@ fun dumpContainer(player: Player, container: core.game.container.Container) {
fun hasHouse(player: Player): Boolean {
return player.houseManager.hasHouse()
}
fun Player.getCutscene(): Cutscene? {
return getAttribute<Cutscene?>(this, Cutscene.ATTRIBUTE_CUTSCENE, null)
}
fun Player.getCutsceneStage(): Int {
return getAttribute(this, Cutscene.ATTRIBUTE_CUTSCENE_STAGE, 0)
}
@@ -1,6 +1,7 @@
package api.events
import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.TeleportManager.TeleportType
@@ -11,3 +12,4 @@ data class LitFireEvent(val logId: Int) : Event
data class InteractionEvent(val target: Node, val option: String) : Event
data class ButtonClickedEvent(val iface: Int, val buttonId: Int) : Event
data class UsedWithEvent(val used: Int, val with: Int) : Event
data class SelfDeath(val killer: Entity) : Event
@@ -113,6 +113,9 @@ class ServerConstants {
@JvmField
var MS_SECRET_KEY = ""
@JvmField
var LOG_CUTSCENE = true
//location names for the ::to command.
val TELEPORT_DESTINATIONS = arrayOf(
arrayOf(Location.create(2974, 4383, 2), "corp", "corporal", "corporeal"),
@@ -10,4 +10,5 @@ object Event {
val Interaction = InteractionEvent::class.java
val ButtonClicked = ButtonClickedEvent::class.java
val UsedWith = UsedWithEvent::class.java
val SelfDeath = SelfDeath::class.java
}
@@ -13,6 +13,7 @@ private const val MIN_DELAY_TICKS = AVG_DELAY_TICKS / 2
private const val MAX_DELAY_TICKS = MIN_DELAY_TICKS + AVG_DELAY_TICKS // window of 60 min centered on 60 min (30 to 90 min)
class RandomEventManager(val player: Player) {
var event: RandomEventNPC? = null
var enabled: Boolean = false
var nextSpawn = 0
val skills = arrayOf("WoodcuttingSkillPulse","FishingPulse","MiningSkillPulse","BoneBuryingOptionPlugin")
@@ -22,6 +23,10 @@ class RandomEventManager(val player: Player) {
}
fun fireEvent() {
if(!enabled){
rollNextSpawn()
return
}
if (player.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS)) {
nextSpawn = GameWorld.ticks + 3000
return
@@ -60,6 +65,7 @@ class RandomEventManager(val player: Player) {
if (player.isArtificial) return
rollNextSpawn()
SystemLogger.logRE("Initialized REManager for ${player.username}")
enabled = true
}
private fun rollNextSpawn() {
@@ -1,239 +1,122 @@
package rs09.game.content.quest.members.thelosttribe
import core.game.content.activity.ActivityPlugin
import core.game.content.activity.CutscenePlugin
import Cutscene
import api.animate
import api.face
import core.game.content.dialogue.FacialExpression
import core.game.content.global.action.DoorActionHandler
import core.game.interaction.MovementPulse
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.emote.Emotes
import core.game.system.task.Pulse
import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.build.DynamicRegion
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import rs09.game.camerautils.PlayerCamera
import rs09.game.world.GameWorld.Pulser
class LostTribeCutscene(player: Player) : Cutscene(player) {
override fun setup() {
setExit(Location.create(3207, 3221, 0))
if(player.settings.isRunToggled){
player.settings.toggleRun()
}
loadRegion(12850)
addNPC(DUKE, 6, 23, Direction.SOUTH)
addNPC(MISTAG, 8, 17, Direction.NORTH)
addNPC(URTAG, 8, 15, Direction.NORTH)
addNPC(SIGMUND, 13, 22, Direction.NORTH_WEST)
}
override fun runStage(stage: Int) {
when(stage)
{
0 -> {
fadeToBlack()
timedUpdate(6)
}
1 -> {
teleport(player, 7, 17)
timedUpdate(2)
}
2 -> {
fadeFromBlack()
moveCamera(8, 26)
rotateCamera(5, 18)
timedUpdate(6)
}
3 -> {
DoorActionHandler.handleDoor(player, getObject(7, 17))
timedUpdate(3)
}
4 -> {
move(player, 7, 22)
move(getNPC(MISTAG)!!, 7, 19)
move(getNPC(URTAG)!!, 7, 18)
timedUpdate(5)
}
5 -> {
move(getNPC(MISTAG)!!, 7, 20)
move(getNPC(URTAG)!!, 6, 21)
timedUpdate(3)
}
6 -> {
player.faceLocation(getNPC(DUKE)!!.location)
playerDialogueUpdate(FacialExpression.FRIENDLY, "Your grace, I present Ur-tag, headman of the Dorgeshuun.")
Emotes.BOW.play(player)
}
7 -> {
move(player, 7, 24)
timedUpdate(5)
}
8 -> {
animate(getNPC(DUKE)!!, Emotes.BOW.animation)
animate(getNPC(URTAG)!!, URTAG_BOW_ANIM)
dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "Welcome, Ur-tag. I am sorry that your race came under suspicion.")
}
9 -> {
dialogueUpdate(DUKE, FacialExpression.ANGRY, "I assure you that the warmongering element has been dealt with.")
moveCamera(9, 22)
rotateCamera(6, 22)
}
10 -> dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL, "I apologize for the damage to your cellar. I will send workers to repair the hole.")
11 -> dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "No, let it stay. It can be a route of commerce between our lands.")
12 -> {
val duke = getNPC(DUKE)!!
face(duke, player.location)
face(player, duke.location)
rotateCamera(6, 22, 300, 3)
moveCamera(11, 22, 325, 3)
dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "${player.username}, Lumbridge is in your debt. Please accept this ring as a token of my thanks.")
}
13 -> dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "It is enchanted to save you in your hour of need.")
14 -> {
move(getNPC(URTAG)!!, 7, 23)
dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL,"I too thank you. Accept the freedom of the Dorgeshuun mines." )
}
15 -> {
dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL, "These are strange times. I never dreamed that I would see the surface, still less that I would be on friendly terms with its people." )
moveCamera(16, 21, 300, 3)
rotateCamera(6, 22, 300, 2)
}
16 -> dialogueUpdate(SIGMUND, FacialExpression.ANGRY, "Prattle on, goblin.")
17 -> {
animate(getNPC(SIGMUND)!!, Emotes.LAUGH.animation)
dialogueUpdate(SIGMUND, FacialExpression.EVIL_LAUGH, "Soon you will be destroyed!")
}
18 -> {
move(getNPC(SIGMUND)!!, 16, 17)
timedUpdate(4)
}
19 -> {
end {
player.questRepository.getQuest("Lost Tribe").setStage(player, 100)
player.questRepository.getQuest("Lost Tribe").finish(player)
}
}
}
}
companion object {
private const val DUKE = 2088
private const val MISTAG = 2089
private const val SIGMUND = 2090
private const val URTAG = 5858
private const val BOW_ANIM = 6000
@Initializable
/**
* Final cutscene for the lost tribe
* @author Ceikry
*/
class LostTribeCutscene(val pl: Player? = null) : CutscenePlugin("Lost Tribe Cutscene",true) {
var camera: PlayerCamera = PlayerCamera(null)
init {
this.player = pl
}
override fun configure() {
region = DynamicRegion.create(12850)
setRegionBase()
registerRegion(region.id)
}
override fun getStartLocation(): Location {
return base.transform(7,17,0)
}
override fun newInstance(p: Player?): ActivityPlugin {
return LostTribeCutscene(player)
}
override fun getSpawnLocation(): Location {
return Location.create(3208, 3213, 0)
}
override fun open() {
player.setAttribute("cutscene:original-loc",player.location)
player.setAttribute("real-end",Location.create(3207, 3221, 0))
player.interfaceManager.removeTabs(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)
if(player.settings.isRunToggled){
player.settings.toggleRun()
}
camera = PlayerCamera(player)
var loc = coords(8,26)
camera.panTo(loc.x,loc.y,300,100)
loc = coords(5,18)
camera.rotateTo(loc.x,loc.y,300,100)
player.faceLocation(coords(7,18))
val mistag = npcs.get(1)
val urtag = npcs.get(2)
val duke = npcs.get(0)
val sigmund = npcs.get(3)
Pulser.submit(object: Pulse(){
var counter = 0
var stage = 0
override fun pulse(): Boolean {
if(!player.isPlaying || !player.isActive) return true
when(counter++){
5 -> DoorActionHandler.handleDoor(player, RegionManager.getObject(coords(7,17)))
7 -> {
player.pulseManager.run(object: MovementPulse(player,coords(7,22),false){
override fun pulse(): Boolean {
return true
}
}, "movement")
mistag.pulseManager.run(object : MovementPulse(mistag,coords(7,19)){//coords(7,20)){
override fun pulse(): Boolean {
return true
}
}, "movement")
urtag.pulseManager.run(object : MovementPulse(urtag,coords(7,18)){
override fun pulse(): Boolean {
return true
}
})
}
12 -> {
mistag.pulseManager.run(object : MovementPulse(mistag,coords(7,20)){//coords(7,20)){
override fun pulse(): Boolean {
return true
}
}, "movement")
urtag.pulseManager.run(object : MovementPulse(urtag,coords(6,21)){
override fun pulse(): Boolean {
return true
}
})
}
15 -> {
player.faceLocation(duke.location)
player.dialogueInterpreter.sendDialogues(player,FacialExpression.FRIENDLY,"Your grace, I present Ur-tag, headman of the","Dorgeshuun.")
Emotes.BOW.play(player)
player.dialogueInterpreter.addAction { player, buttonId ->
Pulser.submit(object: MovementPulse(player,coords(7,24)){
override fun pulse(): Boolean {
return true
}
})
Pulser.submit(object : Pulse(5){
var counter = 0
override fun pulse(): Boolean {
duke.animator.animate(Emotes.BOW.animation)
urtag.animator.animate(Animation(BOW_ANIM))
player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"Welcome, Ur-tag. I am sorry that your race came","under suspicion.")
player.dialogueInterpreter.addAction { player, buttonId ->
player.dialogueInterpreter.sendDialogues(duke,FacialExpression.ANGRY,"I assure you that the warmongering element has been","dealt with.")
loc = coords(9,22)
camera.panTo(loc.x,loc.y,300,2)
loc = coords(6,22)
camera.rotateTo(loc.x,loc.y,300,2)
player.dialogueInterpreter.addAction { player, buttonId ->
player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"I apologize for the damage to your cellar. I will send","workers to repair the hole.")
player.dialogueInterpreter.addAction { player, buttonId ->
player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"No, let it stay. It can be a route of commerce between","our lands.")
player.dialogueInterpreter.addAction { player, buttonId ->
duke.faceLocation(player.location)
player.faceLocation(duke.location)
camera.rotateTo(duke.location.x,duke.location.y,350,4)
loc = coords(11,22)
camera.panTo(loc.x,loc.y,325,3)
loc = coords(6,22)
camera.rotateTo(loc.x,loc.y,300,3)
player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"${player.name}, Lumbridge is in your debt. Please accept","this ring as a token of my thanks.")
player.dialogueInterpreter.addAction { player, buttonId ->
player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"It is enchanted to save you in your hour of need.")
player.dialogueInterpreter.addAction { player, buttonId ->
urtag.pulseManager.run(object: MovementPulse(urtag,coords(7,23)){
override fun pulse(): Boolean {
return true
}
})
urtag.faceLocation(player.location)
player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"I too thank you. Accept the freedom of the Dorgeshuun","mines.")
player.dialogueInterpreter.addAction { player, buttonId ->
player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"These are strange times. I never dreamed that I would","see the surface, still less that I would be on friendly","terms with its people.")
loc = coords(16,21)
camera.panTo(loc.x,loc.y,300,3)
loc = coords(6,22)
camera.rotateTo(loc.x,loc.y,300,2)
player.dialogueInterpreter.addAction { player, buttonId ->
Pulser.submit(object : Pulse(5) {
override fun pulse(): Boolean {
player.dialogueInterpreter.sendDialogues(sigmund,FacialExpression.ANGRY,"Prattle on, goblin.")
player.dialogueInterpreter.addAction { player, buttonId ->
player.dialogueInterpreter.sendDialogues(sigmund,FacialExpression.EVIL_LAUGH,"Soon you will be destroyed!")
sigmund.animator.animate(Emotes.LAUGH.animation)
player.dialogueInterpreter.addAction { player, buttonId ->
loc = coords(16,17)
sigmund.pulseManager.run(object: MovementPulse(sigmund,loc){
override fun pulse(): Boolean {
return true
}
})
Pulser.submit(object : Pulse(4){
override fun pulse(): Boolean {
Pulser.submit(endPulse)
Pulser.submit(object : Pulse(7){
override fun pulse(): Boolean {
player.questRepository.getQuest("Lost Tribe").setStage(player,100)
player.questRepository.getQuest("Lost Tribe").finish(player)
return true
}
})
return true
}
})
private const val URTAG_BOW_ANIM = 6000
}
}
return true
}
})
}
}
}
}
}
}
}
}
return true
}
})
}
loc = coords(7,25)
camera.panTo(loc.x,loc.y,300,3)
return true
}
}
return false
}
})
}
override fun start(player: Player?, login: Boolean, vararg args: Any?): Boolean {
val duke = NPC(DUKE,coords(6,23))
val mistag = NPC(MISTAG,coords(8,17))
val urtag = NPC(URTAG,coords(8,15))
val sigmund = NPC(SIGMUND,coords(13,22))
duke.init()
duke.faceLocation(coords(6,22))
mistag.init()
urtag.init()
sigmund.init()
sigmund.faceLocation(coords(11,18))
npcs.add(duke)
npcs.add(mistag)
npcs.add(urtag)
npcs.add(sigmund)
return super.start(player, login, *args)
}
fun coords(x: Int, y: Int): Location {
return base.transform(x,y,0)
}
}
@@ -1,7 +1,6 @@
package rs09.game.content.quest.members.thelosttribe
import core.game.component.Component
import core.game.content.activity.ActivityManager
import core.game.content.dialogue.FacialExpression
import rs09.game.content.dialogue.DialogueFile
import rs09.tools.END_DIALOGUE
@@ -45,7 +44,8 @@ class MistagLTDialogue(val isGreeting: Boolean, val questStage: Int) : DialogueF
3 -> npc("I will summon Ur-tag, our headman, at once.").also { stage++ }
4 -> {
end()
ActivityManager.start(player,"Lost Tribe Cutscene",false)
LostTribeCutscene(player!!).start()
//ActivityManager.start(player,"Lost Tribe Cutscene",false)
}
}
}
@@ -98,6 +98,7 @@ object ServerConfigParser {
ServerConstants.HOME_LOCATION = parseLocation(data.getString("world.home_location"))
ServerConstants.START_LOCATION = parseLocation(data.getString("world.new_player_location"))
ServerConstants.DAILY_RESTART = data.getBoolean("world.daily_restart")
ServerConstants.LOG_CUTSCENE = data.getBoolean("world.verbose_cutscene", false)
ServerConstants.GRAND_EXCHANGE_DATA_PATH = data.getPath("paths.eco_data")
ServerConstants.CELEDT_DATA_PATH = data.getPath("paths.cele_drop_table_path")
ServerConstants.SERVER_GE_NAME = data.getString("world.name_ge") ?: ServerConstants.SERVER_NAME