Implemented a brand new cutscene system
Converted Lost Tribe's cutscene to the new system
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package core.game.node.entity.combat;
|
package core.game.node.entity.combat;
|
||||||
|
|
||||||
|
import api.events.SelfDeath;
|
||||||
import core.game.container.Container;
|
import core.game.container.Container;
|
||||||
import core.game.container.ContainerType;
|
import core.game.container.ContainerType;
|
||||||
import core.game.node.Node;
|
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
|
e.getImpactHandler().getImpactLog().clear();// check if this needs to be
|
||||||
// before finalize
|
// before finalize
|
||||||
e.getImpactHandler().setDisabledTicks(4);
|
e.getImpactHandler().setDisabledTicks(4);
|
||||||
|
e.dispatch(new SelfDeath(killer));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
|
import Cutscene
|
||||||
import core.cache.def.impl.ItemDefinition
|
import core.cache.def.impl.ItemDefinition
|
||||||
import core.cache.def.impl.SceneryDefinition
|
import core.cache.def.impl.SceneryDefinition
|
||||||
import core.game.component.Component
|
import core.game.component.Component
|
||||||
@@ -1439,3 +1440,11 @@ fun dumpContainer(player: Player, container: core.game.container.Container) {
|
|||||||
fun hasHouse(player: Player): Boolean {
|
fun hasHouse(player: Player): Boolean {
|
||||||
return player.houseManager.hasHouse()
|
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
|
package api.events
|
||||||
|
|
||||||
import core.game.node.Node
|
import core.game.node.Node
|
||||||
|
import core.game.node.entity.Entity
|
||||||
import core.game.node.entity.npc.NPC
|
import core.game.node.entity.npc.NPC
|
||||||
import core.game.node.entity.player.link.TeleportManager.TeleportType
|
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 InteractionEvent(val target: Node, val option: String) : Event
|
||||||
data class ButtonClickedEvent(val iface: Int, val buttonId: Int) : Event
|
data class ButtonClickedEvent(val iface: Int, val buttonId: Int) : Event
|
||||||
data class UsedWithEvent(val used: Int, val with: 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
|
@JvmField
|
||||||
var MS_SECRET_KEY = ""
|
var MS_SECRET_KEY = ""
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var LOG_CUTSCENE = true
|
||||||
|
|
||||||
//location names for the ::to command.
|
//location names for the ::to command.
|
||||||
val TELEPORT_DESTINATIONS = arrayOf(
|
val TELEPORT_DESTINATIONS = arrayOf(
|
||||||
arrayOf(Location.create(2974, 4383, 2), "corp", "corporal", "corporeal"),
|
arrayOf(Location.create(2974, 4383, 2), "corp", "corporal", "corporeal"),
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ object Event {
|
|||||||
val Interaction = InteractionEvent::class.java
|
val Interaction = InteractionEvent::class.java
|
||||||
val ButtonClicked = ButtonClickedEvent::class.java
|
val ButtonClicked = ButtonClickedEvent::class.java
|
||||||
val UsedWith = UsedWithEvent::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)
|
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) {
|
class RandomEventManager(val player: Player) {
|
||||||
var event: RandomEventNPC? = null
|
var event: RandomEventNPC? = null
|
||||||
|
var enabled: Boolean = false
|
||||||
var nextSpawn = 0
|
var nextSpawn = 0
|
||||||
val skills = arrayOf("WoodcuttingSkillPulse","FishingPulse","MiningSkillPulse","BoneBuryingOptionPlugin")
|
val skills = arrayOf("WoodcuttingSkillPulse","FishingPulse","MiningSkillPulse","BoneBuryingOptionPlugin")
|
||||||
|
|
||||||
@@ -22,6 +23,10 @@ class RandomEventManager(val player: Player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun fireEvent() {
|
fun fireEvent() {
|
||||||
|
if(!enabled){
|
||||||
|
rollNextSpawn()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (player.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS)) {
|
if (player.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS)) {
|
||||||
nextSpawn = GameWorld.ticks + 3000
|
nextSpawn = GameWorld.ticks + 3000
|
||||||
return
|
return
|
||||||
@@ -60,6 +65,7 @@ class RandomEventManager(val player: Player) {
|
|||||||
if (player.isArtificial) return
|
if (player.isArtificial) return
|
||||||
rollNextSpawn()
|
rollNextSpawn()
|
||||||
SystemLogger.logRE("Initialized REManager for ${player.username}")
|
SystemLogger.logRE("Initialized REManager for ${player.username}")
|
||||||
|
enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rollNextSpawn() {
|
private fun rollNextSpawn() {
|
||||||
|
|||||||
+91
-208
@@ -1,239 +1,122 @@
|
|||||||
package rs09.game.content.quest.members.thelosttribe
|
package rs09.game.content.quest.members.thelosttribe
|
||||||
|
|
||||||
import core.game.content.activity.ActivityPlugin
|
import Cutscene
|
||||||
import core.game.content.activity.CutscenePlugin
|
import api.animate
|
||||||
|
import api.face
|
||||||
import core.game.content.dialogue.FacialExpression
|
import core.game.content.dialogue.FacialExpression
|
||||||
import core.game.content.global.action.DoorActionHandler
|
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.Player
|
||||||
import core.game.node.entity.player.link.emote.Emotes
|
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.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
|
|
||||||
|
|
||||||
private const val DUKE = 2088
|
class LostTribeCutscene(player: Player) : Cutscene(player) {
|
||||||
private const val MISTAG = 2089
|
override fun setup() {
|
||||||
private const val SIGMUND = 2090
|
setExit(Location.create(3207, 3221, 0))
|
||||||
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){
|
if(player.settings.isRunToggled){
|
||||||
player.settings.toggleRun()
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
camera = PlayerCamera(player)
|
override fun runStage(stage: Int) {
|
||||||
var loc = coords(8,26)
|
when(stage)
|
||||||
camera.panTo(loc.x,loc.y,300,100)
|
{
|
||||||
loc = coords(5,18)
|
0 -> {
|
||||||
camera.rotateTo(loc.x,loc.y,300,100)
|
fadeToBlack()
|
||||||
|
timedUpdate(6)
|
||||||
player.faceLocation(coords(7,18))
|
}
|
||||||
|
1 -> {
|
||||||
val mistag = npcs.get(1)
|
teleport(player, 7, 17)
|
||||||
val urtag = npcs.get(2)
|
timedUpdate(2)
|
||||||
val duke = npcs.get(0)
|
}
|
||||||
val sigmund = npcs.get(3)
|
2 -> {
|
||||||
|
fadeFromBlack()
|
||||||
Pulser.submit(object: Pulse(){
|
moveCamera(8, 26)
|
||||||
var counter = 0
|
rotateCamera(5, 18)
|
||||||
var stage = 0
|
timedUpdate(6)
|
||||||
override fun pulse(): Boolean {
|
}
|
||||||
if(!player.isPlaying || !player.isActive) return true
|
3 -> {
|
||||||
when(counter++){
|
DoorActionHandler.handleDoor(player, getObject(7, 17))
|
||||||
5 -> DoorActionHandler.handleDoor(player, RegionManager.getObject(coords(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 -> {
|
7 -> {
|
||||||
player.pulseManager.run(object: MovementPulse(player,coords(7,22),false){
|
move(player, 7, 24)
|
||||||
override fun pulse(): Boolean {
|
timedUpdate(5)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}, "movement")
|
8 -> {
|
||||||
mistag.pulseManager.run(object : MovementPulse(mistag,coords(7,19)){//coords(7,20)){
|
animate(getNPC(DUKE)!!, Emotes.BOW.animation)
|
||||||
override fun pulse(): Boolean {
|
animate(getNPC(URTAG)!!, URTAG_BOW_ANIM)
|
||||||
return true
|
dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "Welcome, Ur-tag. I am sorry that your race came under suspicion.")
|
||||||
}
|
}
|
||||||
}, "movement")
|
9 -> {
|
||||||
urtag.pulseManager.run(object : MovementPulse(urtag,coords(7,18)){
|
dialogueUpdate(DUKE, FacialExpression.ANGRY, "I assure you that the warmongering element has been dealt with.")
|
||||||
override fun pulse(): Boolean {
|
moveCamera(9, 22)
|
||||||
return true
|
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 -> {
|
12 -> {
|
||||||
mistag.pulseManager.run(object : MovementPulse(mistag,coords(7,20)){//coords(7,20)){
|
val duke = getNPC(DUKE)!!
|
||||||
override fun pulse(): Boolean {
|
face(duke, player.location)
|
||||||
return true
|
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.")
|
||||||
}
|
}
|
||||||
}, "movement")
|
13 -> dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "It is enchanted to save you in your hour of need.")
|
||||||
urtag.pulseManager.run(object : MovementPulse(urtag,coords(6,21)){
|
14 -> {
|
||||||
override fun pulse(): Boolean {
|
move(getNPC(URTAG)!!, 7, 23)
|
||||||
return true
|
dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL,"I too thank you. Accept the freedom of the Dorgeshuun mines." )
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
15 -> {
|
15 -> {
|
||||||
player.faceLocation(duke.location)
|
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." )
|
||||||
player.dialogueInterpreter.sendDialogues(player,FacialExpression.FRIENDLY,"Your grace, I present Ur-tag, headman of the","Dorgeshuun.")
|
moveCamera(16, 21, 300, 3)
|
||||||
Emotes.BOW.play(player)
|
rotateCamera(6, 22, 300, 2)
|
||||||
player.dialogueInterpreter.addAction { player, buttonId ->
|
|
||||||
Pulser.submit(object: MovementPulse(player,coords(7,24)){
|
|
||||||
override fun pulse(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
})
|
16 -> dialogueUpdate(SIGMUND, FacialExpression.ANGRY, "Prattle on, goblin.")
|
||||||
Pulser.submit(object : Pulse(5){
|
17 -> {
|
||||||
var counter = 0
|
animate(getNPC(SIGMUND)!!, Emotes.LAUGH.animation)
|
||||||
override fun pulse(): Boolean {
|
dialogueUpdate(SIGMUND, FacialExpression.EVIL_LAUGH, "Soon you will be destroyed!")
|
||||||
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
|
|
||||||
}
|
}
|
||||||
})
|
18 -> {
|
||||||
urtag.faceLocation(player.location)
|
move(getNPC(SIGMUND)!!, 16, 17)
|
||||||
player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"I too thank you. Accept the freedom of the Dorgeshuun","mines.")
|
timedUpdate(4)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
})
|
19 -> {
|
||||||
Pulser.submit(object : Pulse(4){
|
end {
|
||||||
override fun pulse(): Boolean {
|
player.questRepository.getQuest("Lost Tribe").setStage(player, 100)
|
||||||
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)
|
player.questRepository.getQuest("Lost Tribe").finish(player)
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 {
|
companion object {
|
||||||
val duke = NPC(DUKE,coords(6,23))
|
private const val DUKE = 2088
|
||||||
val mistag = NPC(MISTAG,coords(8,17))
|
private const val MISTAG = 2089
|
||||||
val urtag = NPC(URTAG,coords(8,15))
|
private const val SIGMUND = 2090
|
||||||
val sigmund = NPC(SIGMUND,coords(13,22))
|
private const val URTAG = 5858
|
||||||
duke.init()
|
private const val URTAG_BOW_ANIM = 6000
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,7 +1,6 @@
|
|||||||
package rs09.game.content.quest.members.thelosttribe
|
package rs09.game.content.quest.members.thelosttribe
|
||||||
|
|
||||||
import core.game.component.Component
|
import core.game.component.Component
|
||||||
import core.game.content.activity.ActivityManager
|
|
||||||
import core.game.content.dialogue.FacialExpression
|
import core.game.content.dialogue.FacialExpression
|
||||||
import rs09.game.content.dialogue.DialogueFile
|
import rs09.game.content.dialogue.DialogueFile
|
||||||
import rs09.tools.END_DIALOGUE
|
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++ }
|
3 -> npc("I will summon Ur-tag, our headman, at once.").also { stage++ }
|
||||||
4 -> {
|
4 -> {
|
||||||
end()
|
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.HOME_LOCATION = parseLocation(data.getString("world.home_location"))
|
||||||
ServerConstants.START_LOCATION = parseLocation(data.getString("world.new_player_location"))
|
ServerConstants.START_LOCATION = parseLocation(data.getString("world.new_player_location"))
|
||||||
ServerConstants.DAILY_RESTART = data.getBoolean("world.daily_restart")
|
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.GRAND_EXCHANGE_DATA_PATH = data.getPath("paths.eco_data")
|
||||||
ServerConstants.CELEDT_DATA_PATH = data.getPath("paths.cele_drop_table_path")
|
ServerConstants.CELEDT_DATA_PATH = data.getPath("paths.cele_drop_table_path")
|
||||||
ServerConstants.SERVER_GE_NAME = data.getString("world.name_ge") ?: ServerConstants.SERVER_NAME
|
ServerConstants.SERVER_GE_NAME = data.getString("world.name_ge") ?: ServerConstants.SERVER_NAME
|
||||||
|
|||||||
Reference in New Issue
Block a user