mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-10 10:20:41 -07:00
Merge branch 'werewolf-course-yoink' into 'master'
Werewolf Course See merge request 2009scape/2009scape!1976
This commit is contained in:
commit
a144513751
11 changed files with 939 additions and 10 deletions
|
|
@ -4127,6 +4127,26 @@
|
||||||
"npc_id": "1658",
|
"npc_id": "1658",
|
||||||
"loc_data": "{2595,3087,1,1,1}-"
|
"loc_data": "{2595,3087,1,1,1}-"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"npc_id": "1660",
|
||||||
|
"loc_data": "{3549,9867,0,0,6}-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"npc_id": "1661",
|
||||||
|
"loc_data": "{3540,9873,0,0,1}-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"npc_id": "1662",
|
||||||
|
"loc_data": "{3554,9886,0,1,0}-{3560,9908,0,1,0}-{3565,9865,0,1,0}-{3572,9908,0,1,0}-{3573,9891,0,1,0}-{3577,9875,0,1,0}-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"npc_id": "1663",
|
||||||
|
"loc_data": "{3527,9909,0,1,0}-{3533,9912,0,1,0}-{3540,9892,0,1,0}-{3540,9902,0,1,0}-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"npc_id": "1664",
|
||||||
|
"loc_data": "{3528,9865,0,1,0}-"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"npc_id": "1665",
|
"npc_id": "1665",
|
||||||
"loc_data": "{3544,3462,0,0,1}-"
|
"loc_data": "{3544,3462,0,0,1}-"
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,12 @@ class MorytaniaArea : MapArea {
|
||||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
ZoneBorders(3426, 3191, 3715, 3588), //Morytania overworld
|
ZoneBorders(3426, 3191, 3715, 3588), //Morytania overworld
|
||||||
ZoneBorders(3520, 9856, 3583, 9919) //Werewolf agility course
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areaEnter(entity: Entity) {
|
override fun areaEnter(entity: Entity) {
|
||||||
if (entity is Player && entity !is AIPlayer && (
|
if (entity is Player && entity !is AIPlayer && (
|
||||||
!isQuestComplete(entity, Quests.PRIEST_IN_PERIL) || //not allowed to be anywhere in Morytania
|
!isQuestComplete(entity, Quests.PRIEST_IN_PERIL) //not allowed to be anywhere in Morytania
|
||||||
defineAreaBorders()[1].insideBorder(entity) //Werewolf agility course is not implemented
|
|
||||||
)) {
|
)) {
|
||||||
kickThemOut(entity)
|
kickThemOut(entity)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package content.region.morytania.canifis.dialogue
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import core.api.anyInEquipment
|
||||||
import core.game.dialogue.DialoguePlugin
|
import core.game.dialogue.DialoguePlugin
|
||||||
import core.game.dialogue.FacialExpression
|
import core.game.dialogue.FacialExpression
|
||||||
import core.game.node.entity.npc.NPC
|
import core.game.node.entity.npc.NPC
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.plugin.Initializable
|
import core.plugin.Initializable
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author qmqz
|
* @author qmqz
|
||||||
|
* https://www.youtube.com/watch?v=9u_qJW1eKR0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Initializable
|
@Initializable
|
||||||
|
|
@ -16,29 +19,29 @@ class AgilityBossDialogue(player: Player? = null) : DialoguePlugin(player){
|
||||||
override fun open(vararg args: Any?): Boolean {
|
override fun open(vararg args: Any?): Boolean {
|
||||||
npc = args[0] as NPC
|
npc = args[0] as NPC
|
||||||
|
|
||||||
if(player.equipment.contains(4202,1)) {
|
if(anyInEquipment(player, Items.RING_OF_CHAROS_4202, Items.RING_OF_CHAROSA_6465)) {
|
||||||
player(FacialExpression.ASKING,"How do I use the agility course?").also { stage = 0 }
|
player(FacialExpression.ASKING,"How do I use the agility course?").also { stage = 0 }
|
||||||
} else {
|
} else {
|
||||||
npc(FacialExpression.CHILD_SUSPICIOUS,"Grrr - you don't belong in here, human!").also { stage = 99 }
|
npc(FacialExpression.WEREWOLF_NEUTRAL,"Grrr - you don't belong in here, human!").also { stage = 99 }
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||||
when(stage){
|
when(stage){
|
||||||
0 -> npc(FacialExpression.CHILD_NORMAL,"I'll throw you a stick, which you need to",
|
0 -> npc(FacialExpression.WEREWOLF_NEUTRAL,"I'll throw you a stick, which you need to",
|
||||||
"fetch as quickly as possible, ",
|
"fetch as quickly as possible, ",
|
||||||
"from the area beyond the pipes.").also { stage++ }
|
"from the area beyond the pipes.").also { stage++ }
|
||||||
|
|
||||||
1 -> npc(FacialExpression.CHILD_NORMAL,"Be wary of the deathslide - you must hang by your teeth,",
|
1 -> npc(FacialExpression.WEREWOLF_NEUTRAL,"Be wary of the deathslide - you must hang by your teeth,",
|
||||||
"and if your strength is not up to the job you will",
|
"and if your strength is not up to the job you will",
|
||||||
"fall into a pit of spikes. Also, I would advise not",
|
"fall into a pit of spikes. Also, I would advise not",
|
||||||
"carrying too much extra weight.").also { stage++ }
|
"carrying too much extra weight.").also { stage++ }
|
||||||
|
|
||||||
2 -> npc(FacialExpression.CHILD_NORMAL,"Bring the stick back to the werewolf waiting at",
|
2 -> npc(FacialExpression.WEREWOLF_NEUTRAL,"Bring the stick back to the werewolf waiting at",
|
||||||
"the end of the death slide to get your agility bonus.").also { stage++ }
|
"the end of the death slide to get your agility bonus.").also { stage++ }
|
||||||
|
|
||||||
3 ->npc(FacialExpression.CHILD_NORMAL,"I will throw your stick as soon as you jump onto the",
|
3 ->npc(FacialExpression.WEREWOLF_NEUTRAL,"I will throw your stick as soon as you jump onto the",
|
||||||
"first stone.").also { stage = 99 }
|
"first stone.").also { stage = 99 }
|
||||||
|
|
||||||
99 -> end()
|
99 -> end()
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.dialogue.FacialExpression
|
||||||
|
import core.game.interaction.IntType
|
||||||
|
import core.game.interaction.InteractionListener
|
||||||
|
import core.game.interaction.QueueStrength
|
||||||
|
import core.game.node.entity.Entity
|
||||||
|
import core.game.node.entity.combat.ImpactHandler
|
||||||
|
import core.game.node.entity.impl.ForceMovement
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.entity.skill.Skills
|
||||||
|
import core.game.world.map.Direction
|
||||||
|
import core.game.world.map.Location
|
||||||
|
import core.game.world.map.zone.ZoneBorders
|
||||||
|
import core.game.world.update.flag.context.Animation
|
||||||
|
import core.tools.RandomFunction
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import org.rs09.consts.NPCs
|
||||||
|
import org.rs09.consts.Scenery
|
||||||
|
|
||||||
|
class AgilityCourse : InteractionListener, MapArea {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val LAST_VISITED_STONE_TILE_KEY = "lastVisitedStoneTile"
|
||||||
|
|
||||||
|
val steppingStones = listOf(
|
||||||
|
Location(3538, 9873, 0),
|
||||||
|
Location(3538, 9875, 0), // 1
|
||||||
|
Location(3538, 9877, 0), // 2
|
||||||
|
Location(3540, 9877, 0), // 3
|
||||||
|
Location(3540, 9879, 0), // 4
|
||||||
|
Location(3540, 9881, 0), // 5
|
||||||
|
Location(3540, 9882, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://www.youtube.com/watch?v=JN_1c7r9PVo - Popular courses GOOD!
|
||||||
|
// https://www.youtube.com/watch?v=fmzzLy5fXK4 - a fall location on the other side
|
||||||
|
// https://www.youtube.com/watch?v=RnhjHPuae3Q - best with 2 fall locations nearest and furthest.
|
||||||
|
// https://www.youtube.com/watch?v=_Re3MRhHbZk - middle location fall.
|
||||||
|
// 180 160 140 exp for the 3 failure locations
|
||||||
|
// 200 exp for success
|
||||||
|
val startTile: Location = Location(3528, 9910, 0)
|
||||||
|
val midwayTile1: Location = Location(3528, 9890, 0)
|
||||||
|
val midwayTile2: Location = Location(3528, 9885, 0)
|
||||||
|
val midwayTile3: Location = Location(3528, 9880, 0)
|
||||||
|
val failureTileLeft1: Location = Location(3526, 9888, 0)
|
||||||
|
val failureTileLeft2: Location = Location(3526, 9883, 0)
|
||||||
|
val failureTileLeft3: Location = Location(3526, 9878, 0)
|
||||||
|
val failureTileRight1: Location = Location(3530, 9888, 0)
|
||||||
|
val failureTileRight2: Location = Location(3530, 9883, 0)
|
||||||
|
val failureTileRight3: Location = Location(3530, 9878, 0)
|
||||||
|
val endTile: Location = Location(3528, 9873, 0)
|
||||||
|
|
||||||
|
// anim 767 - Landing on stomach
|
||||||
|
|
||||||
|
fun nearestWerewolfSay(loc: Location, chatText: String) {
|
||||||
|
var werewolfNpc = findLocalNPCs(loc, NPCs.AGILITY_TRAINER_1663).sortedWith { a, b ->
|
||||||
|
a.location.getDistanceSquared(loc) - b.location.getDistanceSquared(loc)
|
||||||
|
}.getOrNull(0)
|
||||||
|
if (werewolfNpc != null) {
|
||||||
|
// println("werewolf ${werewolfNpc.location}")
|
||||||
|
sendChat(werewolfNpc, chatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randomWerewolfSay(): String {
|
||||||
|
return listOf(
|
||||||
|
"Remember - a slow wolf is a hungry wolf!!",
|
||||||
|
"Get on with it - you need your whiskers perking!!!!",
|
||||||
|
"Claws first - think later.",
|
||||||
|
"Imagine the smell of blood in your nostrils!!!",
|
||||||
|
"I never really wanted to be an agility trainer...",
|
||||||
|
"It'll be worth it when you hunt!!",
|
||||||
|
"Let's see those powerful backlegs at work!!",
|
||||||
|
"Let the bloodlust take you!!",
|
||||||
|
"You're the slowest wolf I've ever had the misfortune to witness!!",
|
||||||
|
"When you're done there's a human with your name on it!!",
|
||||||
|
).random()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randomZiplineWerewolfSay(): String {
|
||||||
|
return listOf(
|
||||||
|
"Give my regards to the ground...",
|
||||||
|
"Don't let the spikes or the blood put you off...",
|
||||||
|
"Now for a true test of teeth...",
|
||||||
|
).random()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineListeners() {
|
||||||
|
|
||||||
|
on(Scenery.TRAPDOOR_5131, IntType.SCENERY, "open") { player, node ->
|
||||||
|
replaceScenery(node as core.game.node.scenery.Scenery, Scenery.TRAPDOOR_5132, 20)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ladder Down
|
||||||
|
on(Scenery.TRAPDOOR_5132, IntType.SCENERY, "climb-down") { player, node ->
|
||||||
|
if (!anyInEquipment(player, Items.RING_OF_CHAROS_4202, Items.RING_OF_CHAROSA_6465)) {
|
||||||
|
sendNPCDialogue(player, NPCs.WEREWOLF_1665, "You can't go down there, human. If it wasn't my duty to guard this trapdoor, I would be relieving you of the burden of your life right now.", FacialExpression.WEREWOLF_NEUTRAL)
|
||||||
|
} else {
|
||||||
|
sendNPCDialogue(player, NPCs.WEREWOLF_1665, "Good luck down there, my friend. Remember, to the west is the main agility course, while to the east is a skullball course. ", FacialExpression.WEREWOLF_NEUTRAL)
|
||||||
|
teleport(player, Location(3549, 9865, 0))
|
||||||
|
}
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ladder Up
|
||||||
|
on(Scenery.LADDER_5130, IntType.SCENERY, "climb-up") { player, node ->
|
||||||
|
teleport(player, Location(3543, 3463, 0))
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Stepping stones (destination overrides below)
|
||||||
|
on(Scenery.STEPPING_STONE_35996, IntType.SCENERY, "jump-to") { player, node ->
|
||||||
|
if (!hasLevelStat(player, Skills.AGILITY, 60)) {
|
||||||
|
sendDialogue(player, "You need an Agility level of at least 60 to do this.")
|
||||||
|
return@on false
|
||||||
|
}
|
||||||
|
val arrIndex = steppingStones.indexOf(node.location)
|
||||||
|
ForceMovement.run(player, steppingStones[arrIndex-1], steppingStones[arrIndex], Animation(741), Animation(741), if (arrIndex == 3) Direction.EAST else Direction.NORTH, 20).endAnimation = Animation.RESET
|
||||||
|
rewardXP(player, Skills.AGILITY, 10.0)
|
||||||
|
|
||||||
|
if (arrIndex == 1 && !inInventory(player, Items.STICK_4179)) {
|
||||||
|
val agilityBoss = findLocalNPC(player, NPCs.AGILITY_BOSS_1661)
|
||||||
|
if (agilityBoss != null) {
|
||||||
|
sendChat(agilityBoss, "FETCH!!!!!")
|
||||||
|
face(agilityBoss, Location(3540, 9877, 0))
|
||||||
|
animate(agilityBoss, 6547)
|
||||||
|
produceGroundItem(player, Items.STICK_4179, 1, Location(3543, 9912))
|
||||||
|
spawnProjectile(Location(3540, 9873), Location(3540, 9883), 1158, 0, 0, 1, 60, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hurdles
|
||||||
|
on(intArrayOf(Scenery.HURDLE_5133, Scenery.HURDLE_5134, Scenery.HURDLE_5135), IntType.SCENERY, "jump") { player, node ->
|
||||||
|
if (!hasLevelStat(player, Skills.AGILITY, 60)) {
|
||||||
|
sendDialogue(player, "You need an Agility level of at least 60 to do this.")
|
||||||
|
return@on false
|
||||||
|
}
|
||||||
|
if (player.location.y < node.location.y) {
|
||||||
|
rewardXP(player, Skills.AGILITY, 20.0)
|
||||||
|
ForceMovement.run(player, player.location, player.location.transform(0, 2, 0), Animation(1603), Animation(1603), Direction.NORTH, 20).endAnimation = Animation.RESET
|
||||||
|
} else {
|
||||||
|
sendMessage(player, "You've already jumped over the hurdle.")
|
||||||
|
}
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pipes
|
||||||
|
on(intArrayOf(Scenery.PIPE_5152), IntType.SCENERY, "squeeze-through") { player, node ->
|
||||||
|
if (!hasLevelStat(player, Skills.AGILITY, 60)) {
|
||||||
|
sendDialogue(player, "You need an Agility level of at least 60 to do this.")
|
||||||
|
return@on false
|
||||||
|
}
|
||||||
|
if (player.location.y < node.location.y) {
|
||||||
|
nearestWerewolfSay(Location(3540, 9902), randomWerewolfSay())
|
||||||
|
rewardXP(player, Skills.AGILITY, 15.0)
|
||||||
|
ForceMovement.run(player, player.location, player.location.transform(0, 5, 0), Animation(10580), Animation(844), Direction.NORTH, 10).endAnimation = Animation(10579)
|
||||||
|
} else {
|
||||||
|
sendMessage(player, "You've already squeezed through the pipe.")
|
||||||
|
}
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skull slopes
|
||||||
|
on(intArrayOf(Scenery.SKULL_SLOPE_5136), IntType.SCENERY, "climb-up") { player, node ->
|
||||||
|
if (!hasLevelStat(player, Skills.AGILITY, 60)) {
|
||||||
|
sendDialogue(player, "You need an Agility level of at least 60 to do this.")
|
||||||
|
return@on false
|
||||||
|
}
|
||||||
|
if (player.location.x > node.location.x) {
|
||||||
|
nearestWerewolfSay(Location(3536, 9912), randomWerewolfSay())
|
||||||
|
rewardXP(player, Skills.AGILITY, 25.0)
|
||||||
|
ForceMovement.run(player, player.location, player.location.transform(-2, 0, 0), Animation(2049), Animation(2049), Direction.WEST, 10).endAnimation = Animation.RESET
|
||||||
|
} else {
|
||||||
|
sendMessage(player, "You've already climbed the skull wall.")
|
||||||
|
}
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zip line
|
||||||
|
on(intArrayOf(Scenery.ZIP_LINE_5139, Scenery.ZIP_LINE_5140, Scenery.ZIP_LINE_5141), IntType.SCENERY, "teeth-grip") { player, node ->
|
||||||
|
if (!hasLevelStat(player, Skills.AGILITY, 60)) {
|
||||||
|
sendDialogue(player, "You need an Agility level of at least 60 to do this.")
|
||||||
|
return@on false
|
||||||
|
}
|
||||||
|
|
||||||
|
var successChancePercent = 100.0
|
||||||
|
// "With level 80 in Agility and Strength and a weight of 2 kg or lower, this obstacle will never be failed."
|
||||||
|
// Otherwise, this is up to my decision on whether to torture you
|
||||||
|
if (!(getDynLevel(player, Skills.AGILITY) >= 80 && getDynLevel(player, Skills.STRENGTH) >= 80 && player.settings.weight <= 2.0)) {
|
||||||
|
// All the successes are between 0.0 to 1.0 range
|
||||||
|
val agilitySuccess = RandomFunction.getSkillSuccessChance(0.0, 320.0, 60) / 100// 0 at lvl1, 256 at lvl80 extrapolate to 320 at lvl 99
|
||||||
|
val strengthSuccess = RandomFunction.getSkillSuccessChance(0.0, 320.0, 60) / 100 // 0 at lvl1, 256 at lvl80 extrapolate to 320 at lvl 99
|
||||||
|
val weightSuccess = Math.max(80.0 - player.settings.weight, 0.0) / 100 // 80% chance, minus 1% per weight gain.
|
||||||
|
successChancePercent *= agilitySuccess
|
||||||
|
successChancePercent *= strengthSuccess
|
||||||
|
successChancePercent *= weightSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendMessage(player, "Total Success " + successChancePercent.toString())
|
||||||
|
|
||||||
|
|
||||||
|
// Align player up on the zipline
|
||||||
|
forceMove(player, player.location, Location(3528, 9910, 0), 0,10)
|
||||||
|
face(player, Location(3528, 9915, 0))
|
||||||
|
lock(player, 6)
|
||||||
|
// roll a number, between 0-totalSuccess means you succeed, otherwise between totalSuccess-100 you fail.
|
||||||
|
if(RandomFunction.random(0.0, 100.0) < successChancePercent) { // Success
|
||||||
|
nearestWerewolfSay(Location(3527, 9909), randomZiplineWerewolfSay())
|
||||||
|
queueScript(player, 2, QueueStrength.SOFT) { stage ->
|
||||||
|
when (stage) {
|
||||||
|
0 -> {
|
||||||
|
face(player, Location(3528, 9915, 0))
|
||||||
|
animate(player, 1601)
|
||||||
|
sendMessage(player, "You bravely cling on to the death slide by your teeth ...")
|
||||||
|
return@queueScript delayScript(player, 2)
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
sendChat(player, "WAAAAAARRRGGGHHH!!!!!!")
|
||||||
|
ForceMovement.run(player, startTile, endTile, Animation(1602), Animation(1602), Direction.SOUTH, 60).endAnimation = Animation.RESET
|
||||||
|
return@queueScript delayScript(player, 8)
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
rewardXP(player, Skills.AGILITY, 200.0)
|
||||||
|
sendMessage(player, ".. and land safely on your feet.")
|
||||||
|
teleport(player, endTile)
|
||||||
|
return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
else -> return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Based on total success, find where to land. If you had a lower chance, you get the early drop and lower XP.
|
||||||
|
var finalTile = endTile
|
||||||
|
var animTicks = 6
|
||||||
|
var fallTile = endTile
|
||||||
|
var rewardXP = 200.0
|
||||||
|
if (successChancePercent <= 20.0) {
|
||||||
|
finalTile = midwayTile1
|
||||||
|
fallTile = arrayOf(failureTileLeft1, failureTileRight1).random()
|
||||||
|
animTicks = 4
|
||||||
|
rewardXP = 140.0
|
||||||
|
}
|
||||||
|
if (successChancePercent > 20.0 && successChancePercent <= 40.0 ) {
|
||||||
|
finalTile = midwayTile2
|
||||||
|
fallTile = arrayOf(failureTileLeft2, failureTileRight2).random()
|
||||||
|
animTicks = 5
|
||||||
|
rewardXP = 160.0
|
||||||
|
}
|
||||||
|
if (successChancePercent > 40.0) {
|
||||||
|
finalTile = midwayTile3
|
||||||
|
fallTile = arrayOf(failureTileLeft3, failureTileRight3).random()
|
||||||
|
animTicks = 6
|
||||||
|
rewardXP = 180.0
|
||||||
|
}
|
||||||
|
|
||||||
|
nearestWerewolfSay(Location(3527, 9909), randomZiplineWerewolfSay())
|
||||||
|
queueScript(player, 2, QueueStrength.SOFT) { stage ->
|
||||||
|
when (stage) {
|
||||||
|
0 -> {
|
||||||
|
face(player, Location(3528, 9915, 0))
|
||||||
|
animate(player, 1601)
|
||||||
|
sendMessage(player, "You bravely cling on to the death slide by your teeth ...")
|
||||||
|
return@queueScript delayScript(player, 2)
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
sendChat(player, "WAAAAAARRRGGGHHH!!!!!!")
|
||||||
|
ForceMovement.run(player, startTile, finalTile, Animation(1602), Animation(1602), Direction.SOUTH, 60).endAnimation = Animation(767)
|
||||||
|
return@queueScript delayScript(player, animTicks)
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
rewardXP(player, Skills.AGILITY, rewardXP)
|
||||||
|
sendMessage(player, ".. only to fall from a great height!")
|
||||||
|
teleport(player, fallTile)
|
||||||
|
// Can't get this to chain animations.
|
||||||
|
//ForceMovement.run(player, finalTile, fallTile, Animation(767), Animation(767), Direction.SOUTH, 60).endAnimation = Animation(767)
|
||||||
|
return@queueScript delayScript(player, 2)
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
teleport(player, fallTile)
|
||||||
|
player.impactHandler.manualHit(player, (1..30).random(), ImpactHandler.HitsplatType.NORMAL)
|
||||||
|
return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
else -> return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineDestinationOverrides() {
|
||||||
|
setDest(IntType.SCENERY, intArrayOf(Scenery.STEPPING_STONE_35996),"jump-to"){ player, node ->
|
||||||
|
val arrIndex = steppingStones.indexOf(node.location)
|
||||||
|
return@setDest steppingStones[arrIndex - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||||
|
// Area of the zipline.
|
||||||
|
return arrayOf(ZoneBorders(3527, 9876, 3529, 9907))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||||
|
// In case you log out during the zipline of death slide, you won't be left on it.
|
||||||
|
// You lose that XP though...
|
||||||
|
if (entity is Player) {
|
||||||
|
if (logout) {
|
||||||
|
teleport(entity, endTile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.dialogue.*
|
||||||
|
import core.game.interaction.IntType
|
||||||
|
import core.game.interaction.InteractionListener
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.skill.Skills
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import org.rs09.consts.NPCs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://www.youtube.com/watch?v=mIKPpc30XBQ - What stick
|
||||||
|
*/
|
||||||
|
class AgilityTrainerDialogue : InteractionListener {
|
||||||
|
|
||||||
|
override fun defineListeners() {
|
||||||
|
on(NPCs.AGILITY_TRAINER_1664, IntType.NPC, "give-stick") { player, node ->
|
||||||
|
if (inInventory(player, Items.STICK_4179)) {
|
||||||
|
removeItem(player, Items.STICK_4179)
|
||||||
|
rewardXP(player, Skills.AGILITY, 190.0)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
DialogueLabeller.open(player, object : DialogueLabeller() {
|
||||||
|
override fun addConversation() {
|
||||||
|
assignToIds(NPCs.AGILITY_TRAINER_1664)
|
||||||
|
// shorts/TO-vdlyOa3E
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "Have you brought the stick yet?")
|
||||||
|
player("What stick?")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "Come on, get round that course - I need something to chew!")
|
||||||
|
}
|
||||||
|
}, node as NPC)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.dialogue.*
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.plugin.Initializable
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import org.rs09.consts.NPCs
|
||||||
|
|
||||||
|
@Initializable
|
||||||
|
class SkullballBossDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||||
|
override fun newInstance(player: Player): DialoguePlugin {
|
||||||
|
return SkullballBossDialogue(player)
|
||||||
|
}
|
||||||
|
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||||
|
openDialogue(player, SkullballBossDialogueFile(), npc)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
override fun getIds(): IntArray {
|
||||||
|
return intArrayOf(NPCs.SKULLBALL_BOSS_1660)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class SkullballBossDialogueFile : DialogueLabeller() {
|
||||||
|
override fun addConversation() {
|
||||||
|
assignToIds(NPCs.SKULLBALL_BOSS_1660)
|
||||||
|
|
||||||
|
exec { player, npc ->
|
||||||
|
if(!anyInEquipment(player, Items.RING_OF_CHAROS_4202, Items.RING_OF_CHAROSA_6465)) {
|
||||||
|
goto("ishuman")
|
||||||
|
} else if (getAttribute<NPC?>(player, SkullballCourse.attributeSkullballInstance, null) != null) {
|
||||||
|
goto("skullballinprogress")
|
||||||
|
} else {
|
||||||
|
goto("noskullball")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label("ishuman")
|
||||||
|
player(ChatAnim.WEREWOLF_SUSPICIOUS, "Grrr - you don't belong in here, human!")
|
||||||
|
|
||||||
|
label("skullballinprogress")
|
||||||
|
options(
|
||||||
|
DialogueOption("explainskullball", "What are the instructions for using the skullball course?", expression=ChatAnim.THINKING),
|
||||||
|
DialogueOption("lostskullball", "I seem to have lost my ball - can I have another one?", expression=ChatAnim.THINKING),
|
||||||
|
DialogueOption("clearskullball", "I give up, I can't do it - take my ball away.", expression=ChatAnim.NEUTRAL),
|
||||||
|
)
|
||||||
|
|
||||||
|
label("noskullball")
|
||||||
|
options(
|
||||||
|
DialogueOption("startskullball", "I would like to do the skullball course.", expression=ChatAnim.NEUTRAL),
|
||||||
|
DialogueOption("explainskullball", "What are the instructions for using the skullball course?", expression=ChatAnim.THINKING),
|
||||||
|
)
|
||||||
|
|
||||||
|
label("startskullball")
|
||||||
|
exec { player, npc ->
|
||||||
|
SkullballCourse.startBall(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
label("lostskullball")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "No problem, here's another one. You'll have to start from the beginning again, but the timer will be restarted too.")
|
||||||
|
exec { player, npc ->
|
||||||
|
SkullballCourse.clearBall(player)
|
||||||
|
SkullballCourse.startBall(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
label("clearskullball")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "Oh dear, such a defeatist.")
|
||||||
|
exec { player, npc ->
|
||||||
|
SkullballCourse.clearBall(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
label("explainskullball")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "The skullball comes out of one of these four spawnholes. Just kick the ball through the middle of each goal, through the skeleton's feet.")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "There are 10 goals, which you must complete in order, and one final goal.")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "An arrow will point to your ball, just in case lots of people are using the course at the same time as yourself.")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "The better your time, the more agility XP you will be awarded. The timer starts when you score your first goal.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,328 @@
|
||||||
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import content.region.morytania.werewolfagility.SkullballCourse.Companion.skullballGoals
|
||||||
|
import core.api.*
|
||||||
|
import core.game.interaction.InteractionListener
|
||||||
|
import core.game.interaction.QueueStrength
|
||||||
|
import core.game.node.entity.Entity
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.npc.NPCBehavior
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.entity.skill.Skills
|
||||||
|
import core.game.world.map.Direction
|
||||||
|
import core.game.world.map.Location
|
||||||
|
import core.game.world.map.RegionManager
|
||||||
|
import core.game.world.map.zone.ZoneBorders
|
||||||
|
import core.game.world.update.flag.context.Animation
|
||||||
|
import org.rs09.consts.NPCs
|
||||||
|
|
||||||
|
class SkullballCourse : MapArea {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val attributeSkullballInstance = "skullball-instance"
|
||||||
|
val attributeSkullballCurrentGoal = "skullball-currentgoal" // 0 to 10
|
||||||
|
val attributeSkullballStartTime = "skullball-starttime"
|
||||||
|
|
||||||
|
val skullballGoalIface = 379
|
||||||
|
|
||||||
|
val startingBall = arrayOf(
|
||||||
|
Location.create(3552, 9859),
|
||||||
|
Location.create(3554, 9860),
|
||||||
|
Location.create(3555, 9860),
|
||||||
|
Location.create(3557, 9859),
|
||||||
|
)
|
||||||
|
/** Array of ZoneBorders with Skullball Goals */
|
||||||
|
val skullballGoals = arrayOf(
|
||||||
|
ZoneBorders(3555,9870,3555,9870), // rot 0
|
||||||
|
ZoneBorders(3556,9883,3556,9883),
|
||||||
|
ZoneBorders(3558,9891,3558,9891),
|
||||||
|
ZoneBorders(3557,9900,3557,9900),
|
||||||
|
ZoneBorders(3558,9906,3558,9906),
|
||||||
|
ZoneBorders(3563,9911,3563,9911), // rot 1
|
||||||
|
ZoneBorders(3575,9905,3575,9905), // rot 2
|
||||||
|
ZoneBorders(3574,9888,3574,9888),
|
||||||
|
ZoneBorders(3575,9878,3575,9878),
|
||||||
|
ZoneBorders(3568,9864,3568,9864), // rot 3
|
||||||
|
ZoneBorders(3563,9865,3563,9865), // End goal tunnel
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Extract Location from ZoneBorders **/
|
||||||
|
fun extractLoc(z :ZoneBorders): Location {
|
||||||
|
return Location(z.northEastX, z.northEastY, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a skullball for the player to kick around. */
|
||||||
|
fun startBall(player: Player) {
|
||||||
|
if (getAttribute<NPC?>(player, attributeSkullballInstance, null) == null) {
|
||||||
|
val npc = NPC(NPCs.SKULLBALL_1659)
|
||||||
|
setAttribute(npc, "target", player)
|
||||||
|
setAttribute(player, attributeSkullballInstance, npc)
|
||||||
|
npc.isRespawn = false
|
||||||
|
npc.isWalks = false
|
||||||
|
npc.location = startingBall.random()
|
||||||
|
npc.direction = Direction.NORTH
|
||||||
|
npc.walkRadius = 100
|
||||||
|
npc.init()
|
||||||
|
clearHintIcon(player)
|
||||||
|
registerHintIcon(player, npc)
|
||||||
|
npc.lock(5)
|
||||||
|
// Force walk doesn't work here because this npc isn't flagged to be forced walked.
|
||||||
|
npc.walkingQueue.reset()
|
||||||
|
val newLoc = npc.location.transform(Location(0, 4, 0))
|
||||||
|
npc.walkingQueue.addPath(newLoc.x, newLoc.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears the skullball from the world for that player. */
|
||||||
|
fun clearBall(player: Player) {
|
||||||
|
val npcBall = getAttribute<NPC?>(player, attributeSkullballInstance, null)
|
||||||
|
if (npcBall != null) {
|
||||||
|
clearHintIcon(player)
|
||||||
|
removeAttribute(npcBall, "target")
|
||||||
|
npcBall.clear()
|
||||||
|
removeAttribute(player, attributeSkullballCurrentGoal)
|
||||||
|
removeAttribute(player, attributeSkullballStartTime)
|
||||||
|
removeAttribute(player, attributeSkullballInstance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nearestWerewolfSay(loc: Location, chatText: String) {
|
||||||
|
var werewolfNpc = findLocalNPCs(loc, 40).filter { npc ->
|
||||||
|
npc.id == NPCs.SKULLBALL_TRAINER_1662
|
||||||
|
}.sortedWith { a, b ->
|
||||||
|
a.location.getDistanceSquared(loc) - b.location.getDistanceSquared(loc)
|
||||||
|
}.getOrNull(0)
|
||||||
|
if (werewolfNpc != null) {
|
||||||
|
// println("werewolf ${werewolfNpc.location}")
|
||||||
|
sendChat(werewolfNpc, chatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randomWerewolfSay(): String {
|
||||||
|
return listOf(
|
||||||
|
"You have truly gifted paws!",
|
||||||
|
"I've never seen anything like it!",
|
||||||
|
"Claws first - think later.",
|
||||||
|
"You need a few more skullball lessons.",
|
||||||
|
"Keep it up!",
|
||||||
|
"Don't give up the day job!",
|
||||||
|
"Look at @g[her,him] go!",
|
||||||
|
"Pathetic!",
|
||||||
|
"What - a - goal !!!",
|
||||||
|
"That was just plain lucky.",
|
||||||
|
).random()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randomZiplineWerewolfSay(): String {
|
||||||
|
return listOf(
|
||||||
|
"Give my regards to the ground...",
|
||||||
|
"Don't let the spikes or the blood put you off...",
|
||||||
|
"Now for a true test of teeth...",
|
||||||
|
).random()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||||
|
return skullballGoals.copyOf(skullballGoals.lastIndex).filterNotNull().toTypedArray() // remove last goal
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||||
|
// When leaving the area with the goals, the scenery of the goal is one tile adjacent to it.
|
||||||
|
val surroundingScenery =
|
||||||
|
getScenery(entity.location.transform(1,0,0)) ?:
|
||||||
|
getScenery(entity.location.transform(0,1,0)) ?:
|
||||||
|
getScenery(entity.location.transform(-1,0,0)) ?:
|
||||||
|
getScenery(entity.location.transform(0,-1,0))
|
||||||
|
// If that tile is the actual goal,
|
||||||
|
if (surroundingScenery != null && surroundingScenery.id == 5146) {
|
||||||
|
animateScenery(surroundingScenery, 1598) // anim 1599 is when skullball enters from behind.
|
||||||
|
|
||||||
|
val player = getAttribute<Player?>(entity, "target", null)
|
||||||
|
if (player == null) { return }
|
||||||
|
// On the first goal, start the time.
|
||||||
|
if (surroundingScenery.location.equals(extractLoc(skullballGoals[0]))) {
|
||||||
|
if (getAttribute<Long?>(player, attributeSkullballStartTime, null) == null) {
|
||||||
|
setAttribute(player, attributeSkullballStartTime, System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val currGoal = getAttribute(player, attributeSkullballCurrentGoal, 0)
|
||||||
|
if (surroundingScenery.location.equals(extractLoc(skullballGoals[currGoal]))) {
|
||||||
|
setAttribute(player, attributeSkullballCurrentGoal, currGoal + 1)
|
||||||
|
val nextGoal = getAttribute(player, attributeSkullballCurrentGoal, 0)
|
||||||
|
clearHintIcon(player)
|
||||||
|
if (nextGoal < skullballGoals.size) {
|
||||||
|
registerHintIcon(player, getScenery(extractLoc(skullballGoals[nextGoal]))!!)
|
||||||
|
}
|
||||||
|
if (nextGoal < skullballGoals.size) {
|
||||||
|
nearestWerewolfSay(entity.location, randomWerewolfSay())
|
||||||
|
} else {
|
||||||
|
nearestWerewolfSay(entity.location, "@g[He,She] shoots - @g[He,She] scores!!!!!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkullballBehavior : NPCBehavior(NPCs.SKULLBALL_1659), InteractionListener, MapArea {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/** Generates a movement queue for a kicked/pushed thing. Bounces against walls. **/
|
||||||
|
private fun generatePath(npc: NPC, kickDirection: Location, distanceToMove: Int) {
|
||||||
|
npc.walkingQueue.reset()
|
||||||
|
var moveDirection = kickDirection
|
||||||
|
var nextTile = npc.location
|
||||||
|
// For each tile to move,
|
||||||
|
for (i in 1..distanceToMove) {
|
||||||
|
nextTile = nextTile.transform(moveDirection)
|
||||||
|
if (!(RegionManager.isTeleportPermitted(nextTile) || // walkable square
|
||||||
|
nextTile.equals(3563, 9865, 0) || // final goal
|
||||||
|
nextTile.equals(3563, 9866, 0) || // final goal
|
||||||
|
getScenery(nextTile)?.id == 5146 // skeleton goal
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Tile is blocked, reverse ball direction and set a walkingQueue Path.
|
||||||
|
moveDirection = Location(-moveDirection.x, -moveDirection.y, moveDirection.z)
|
||||||
|
nextTile = nextTile.transform(moveDirection)
|
||||||
|
npc.walkingQueue.addPath(nextTile.x, nextTile.y)
|
||||||
|
nextTile = nextTile.transform(moveDirection)
|
||||||
|
if (nextTile.equals(3563, 9866, 0)) { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
npc.walkingQueue.addPath(nextTile.x, nextTile.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Moves the ball a certain distance (1,4,9 as authentic). */
|
||||||
|
fun moveBall(player: Player, ballNpc: NPC, distance: Int) {
|
||||||
|
if (getAttribute<Player?>(ballNpc, "target", null)?.username == player.username) {
|
||||||
|
clearHintIcon(player)
|
||||||
|
registerHintIcon(player, ballNpc)
|
||||||
|
animate(player, 1606)
|
||||||
|
generatePath(ballNpc, Location.getDelta(player.location, ballNpc.location), distance)
|
||||||
|
} else {
|
||||||
|
sendMessage(player, "That is not your skullball.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Show current goal to score. */
|
||||||
|
fun showGoal(player: Player, ballNpc: NPC) {
|
||||||
|
if (getAttribute<Player?>(ballNpc, "target", null)?.username == player.username) {
|
||||||
|
val currGoal = getAttribute(player, SkullballCourse.attributeSkullballCurrentGoal, 0)
|
||||||
|
clearHintIcon(player)
|
||||||
|
if (currGoal < skullballGoals.size) {
|
||||||
|
registerHintIcon(player, getScenery(SkullballCourse.extractLoc(SkullballCourse.skullballGoals[currGoal]))!!)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendMessage(player, "That is not your skullball.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculate the amount of time from the first goal to the final goal. */
|
||||||
|
fun calcTime(player: Player) : String {
|
||||||
|
val startTime = getAttribute(player, SkullballCourse.attributeSkullballStartTime, System.currentTimeMillis())
|
||||||
|
val endTime = System.currentTimeMillis()
|
||||||
|
val timeDiffInSecs = (endTime - startTime) / 1000
|
||||||
|
val finalMins = timeDiffInSecs / 60
|
||||||
|
val finalSecs = timeDiffInSecs % 60
|
||||||
|
return String.format("%01d:%02d", finalMins, finalSecs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Calculate the amount exp earned when kicked into the last goal. */
|
||||||
|
fun calcExp(player: Player) : Int {
|
||||||
|
val startTime = getAttribute(player, SkullballCourse.attributeSkullballStartTime, System.currentTimeMillis())
|
||||||
|
val endTime = System.currentTimeMillis()
|
||||||
|
val timeDiffInSecs = (((endTime - startTime) / 1000) - 240).toInt().coerceAtLeast(0)
|
||||||
|
return (750 - timeDiffInSecs / 3).coerceAtLeast(0) // Kotlin what the fuck is this function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var clearTime = 0
|
||||||
|
|
||||||
|
override fun onRemoval(self: NPC) {
|
||||||
|
clearTime = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tick(self: NPC): Boolean {
|
||||||
|
// You have 800 ticks = 8 mins to kick the ball into the goal.
|
||||||
|
if (clearTime++ > 800) {
|
||||||
|
clearTime = 0
|
||||||
|
val player = getAttribute<Player?>(self, "target", null)
|
||||||
|
if (player != null) {
|
||||||
|
removeAttribute(player, SkullballCourse.attributeSkullballInstance)
|
||||||
|
}
|
||||||
|
removeAttribute(self, "target")
|
||||||
|
self.clear()
|
||||||
|
}
|
||||||
|
if (!self.location.equals(3563, 9866, 0)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
val player = getAttribute<Player?>(self, "target", null)
|
||||||
|
if (player == null) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (getAttribute(player, SkullballCourse.attributeSkullballCurrentGoal, 0) != 10) {
|
||||||
|
sendMessage(player, "You did not score all the goals.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
sendMessage(player, "Well done - you've finished the skullball course!!!")
|
||||||
|
lock(player, Animation(1605).duration)
|
||||||
|
queueScript(player, 0, QueueStrength.SOFT) { stage: Int ->
|
||||||
|
when (stage) {
|
||||||
|
0 -> {
|
||||||
|
animate(player, 1605)
|
||||||
|
return@queueScript delayScript(player, Animation(1605).duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
setInterfaceText(player, calcTime(player), SkullballCourse.skullballGoalIface, 5)
|
||||||
|
val finalExp = calcExp(player)
|
||||||
|
setInterfaceText(player, finalExp.toString(), SkullballCourse.skullballGoalIface, 6)
|
||||||
|
rewardXP(player, Skills.AGILITY, finalExp.toDouble())
|
||||||
|
|
||||||
|
openInterface(player, SkullballCourse.skullballGoalIface)
|
||||||
|
|
||||||
|
removeAttribute(player, SkullballCourse.attributeSkullballInstance)
|
||||||
|
removeAttribute(player, SkullballCourse.attributeSkullballCurrentGoal)
|
||||||
|
removeAttribute(player, SkullballCourse.attributeSkullballStartTime)
|
||||||
|
removeAttribute(self, "target")
|
||||||
|
self.clear()
|
||||||
|
return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineListeners() {
|
||||||
|
on(NPCs.SKULLBALL_1659, NPC, "tap") { player, node ->
|
||||||
|
moveBall(player, node as NPC, 1)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
on(NPCs.SKULLBALL_1659, NPC, "kick") { player, node ->
|
||||||
|
moveBall(player, node as NPC, 4)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
on(NPCs.SKULLBALL_1659, NPC, "shoot") { player, node ->
|
||||||
|
moveBall(player, node as NPC, 9)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
|
||||||
|
on(NPCs.SKULLBALL_1659, NPC, "show-goal") { player, node ->
|
||||||
|
showGoal(player, node as NPC)
|
||||||
|
return@on true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||||
|
return arrayOf(getRegionBorders(14234))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||||
|
if (entity is Player) {
|
||||||
|
SkullballCourse.clearBall(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.dialogue.ChatAnim
|
||||||
|
import core.game.dialogue.DialogueLabeller
|
||||||
|
import core.game.dialogue.DialoguePlugin
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.plugin.Initializable
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import org.rs09.consts.NPCs
|
||||||
|
|
||||||
|
@Initializable
|
||||||
|
class SkullballTrainerDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||||
|
override fun newInstance(player: Player): DialoguePlugin {
|
||||||
|
return SkullballTrainerDialogue(player)
|
||||||
|
}
|
||||||
|
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||||
|
openDialogue(player, SkullballTrainerDialogueFile(), npc)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
override fun getIds(): IntArray {
|
||||||
|
return intArrayOf(NPCs.SKULLBALL_TRAINER_1662)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class SkullballTrainerDialogueFile : DialogueLabeller() {
|
||||||
|
override fun addConversation() {
|
||||||
|
assignToIds(NPCs.SKULLBALL_TRAINER_1662)
|
||||||
|
|
||||||
|
exec { player, npc ->
|
||||||
|
if(!anyInEquipment(player, Items.RING_OF_CHAROS_4202, Items.RING_OF_CHAROSA_6465)) {
|
||||||
|
goto("ishuman")
|
||||||
|
} else if (getAttribute<NPC?>(player, SkullballCourse.attributeSkullballInstance, null) != null) {
|
||||||
|
goto("skullballinprogress")
|
||||||
|
} else {
|
||||||
|
goto("noskullball")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label("ishuman")
|
||||||
|
player(ChatAnim.WEREWOLF_SUSPICIOUS, "Grrr - you don't belong in here, human!")
|
||||||
|
|
||||||
|
label("noskullball")
|
||||||
|
player(ChatAnim.THINKING, "What is this place?")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "This is the Skullball Course")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "Go talk to the boss at the beginning of the course if you'd like a go.")
|
||||||
|
|
||||||
|
// Eovq4QBY39c
|
||||||
|
label("skullballinprogress")
|
||||||
|
player(ChatAnim.THINKING, "How many goals have I got left?")
|
||||||
|
exec { player, npc ->
|
||||||
|
if (getAttribute(player, SkullballCourse.attributeSkullballCurrentGoal, 0) != 10) {
|
||||||
|
goto("xgoals")
|
||||||
|
} else {
|
||||||
|
goto("finalgoal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label("xgoals")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "You have ${10 - getAttribute(player!!,
|
||||||
|
SkullballCourse.attributeSkullballCurrentGoal, 0)} goals left to complete.")
|
||||||
|
|
||||||
|
label("finalgoal")
|
||||||
|
npc(ChatAnim.WEREWOLF_NEUTRAL, "You have the final goal left to complete.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package content.region.morytania.werewolfagility
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.dialogue.DialogueBuilder
|
||||||
|
import core.game.dialogue.DialogueBuilderFile
|
||||||
|
import core.game.dialogue.DialoguePlugin
|
||||||
|
import core.game.dialogue.FacialExpression
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.plugin.Initializable
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import org.rs09.consts.NPCs
|
||||||
|
|
||||||
|
@Initializable
|
||||||
|
class WerewolfGuardDialogue (player: Player? = null) : DialoguePlugin(player) {
|
||||||
|
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||||
|
openDialogue(player, WerewolfGuardDialogueFile(), npc)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
override fun newInstance(player: Player): DialoguePlugin {
|
||||||
|
return WerewolfGuardDialogue(player)
|
||||||
|
}
|
||||||
|
override fun getIds(): IntArray {
|
||||||
|
return intArrayOf(NPCs.WEREWOLF_1665)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WerewolfGuardDialogueFile : DialogueBuilderFile() {
|
||||||
|
override fun create(b: DialogueBuilder) {
|
||||||
|
b.onPredicate { _ -> true }
|
||||||
|
.playerl(FacialExpression.FRIENDLY, "What's beneath the trapdoor?")
|
||||||
|
.branch { player -> if (anyInEquipment(player, Items.RING_OF_CHAROS_4202, Items.RING_OF_CHAROSA_6465)) { 1 } else { 0 } }
|
||||||
|
.let { branch ->
|
||||||
|
branch.onValue(0)
|
||||||
|
// .let { builder ->
|
||||||
|
// val returnJoin = b.placeholder()
|
||||||
|
// returnJoin.builder()
|
||||||
|
// .manualStage { _, player, _, _ ->
|
||||||
|
// sendNPCDialogue(player, NPCs.WEREWOLF_1665, "Face. " + expCount, expCount)
|
||||||
|
// expCount++
|
||||||
|
// }
|
||||||
|
// .goto(returnJoin)
|
||||||
|
// }
|
||||||
|
.npcl(FacialExpression.WEREWOLF_NEUTRAL, "That's none of your business, human, and I'll never tell.")
|
||||||
|
.playerl(FacialExpression.FRIENDLY, "Oh, come on - I'm only curious.")
|
||||||
|
.npcl(FacialExpression.WEREWOLF_NEUTRAL, "If it wasn't my duty to stand here and guard our agility course from the likes of you, I would be relieving you of your life right now.")
|
||||||
|
.playerl(FacialExpression.THINKING, "So it's an agility course, then?")
|
||||||
|
.npcl(FacialExpression.WEREWOLF_SUSPICIOUS, "No ... yes ... oh blast - you didn't hear me say anything, right?")
|
||||||
|
.playerl(FacialExpression.FRIENDLY, "No problem. Can I come in?")
|
||||||
|
.npcl(FacialExpression.WEREWOLF_NEUTRAL, "No, human - it's werewolves only.")
|
||||||
|
.end()
|
||||||
|
|
||||||
|
branch.onValue(1)
|
||||||
|
.npcl(FacialExpression.WEREWOLF_NEUTRAL, "It's an agility course designed for lycanthropes like ourselves, my friend.")
|
||||||
|
.playerl(FacialExpression.FRIENDLY, "Can I come in and use it?")
|
||||||
|
.npc(FacialExpression.WEREWOLF_HAPPY, "Certainly. The cavern contains two courses - on the", "west side is a level 60 Agility Course, and on the east", "side is a level 25 Skullball Course.")
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -89,6 +89,13 @@ public enum FacialExpression {
|
||||||
STRUGGLE(9865), //TODO: More?
|
STRUGGLE(9865), //TODO: More?
|
||||||
//9855-9857 are like disgusted? does it just repeat after this?
|
//9855-9857 are like disgusted? does it just repeat after this?
|
||||||
|
|
||||||
|
//Chatheads for werewolves
|
||||||
|
WEREWOLF_SAD(6550),
|
||||||
|
WEREWOLF_NEUTRAL(6551),
|
||||||
|
WEREWOLF_SUSPICIOUS(6552),
|
||||||
|
WEREWOLF_THINKING(6553),
|
||||||
|
WEREWOLF_HAPPY(6555),
|
||||||
|
|
||||||
//Child Chathead?
|
//Child Chathead?
|
||||||
CHILD_ANGRY(7168),
|
CHILD_ANGRY(7168),
|
||||||
CHILD_SIDE_EYE(7169),
|
CHILD_SIDE_EYE(7169),
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,17 @@ public final class Location extends Node {
|
||||||
return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance between you and the other squared. This removes the square root for comparison functions.
|
||||||
|
* @param other The other location.
|
||||||
|
* @return The amount of distance between you and other, squared.
|
||||||
|
*/
|
||||||
|
public int getDistanceSquared(Location other) {
|
||||||
|
int xdiff = this.getX() - other.getX();
|
||||||
|
int ydiff = this.getY() - other.getY();
|
||||||
|
return xdiff * xdiff + ydiff * ydiff;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the distance between the first and the second specified distance.
|
* Returns the distance between the first and the second specified distance.
|
||||||
* @param first The first location.
|
* @param first The first location.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue