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",
|
||||
"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",
|
||||
"loc_data": "{3544,3462,0,0,1}-"
|
||||
|
|
|
|||
|
|
@ -17,14 +17,12 @@ class MorytaniaArea : MapArea {
|
|||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(
|
||||
ZoneBorders(3426, 3191, 3715, 3588), //Morytania overworld
|
||||
ZoneBorders(3520, 9856, 3583, 9919) //Werewolf agility course
|
||||
)
|
||||
}
|
||||
|
||||
override fun areaEnter(entity: Entity) {
|
||||
if (entity is Player && entity !is AIPlayer && (
|
||||
!isQuestComplete(entity, Quests.PRIEST_IN_PERIL) || //not allowed to be anywhere in Morytania
|
||||
defineAreaBorders()[1].insideBorder(entity) //Werewolf agility course is not implemented
|
||||
!isQuestComplete(entity, Quests.PRIEST_IN_PERIL) //not allowed to be anywhere in Morytania
|
||||
)) {
|
||||
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.FacialExpression
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.Items
|
||||
|
||||
/**
|
||||
* @author qmqz
|
||||
* https://www.youtube.com/watch?v=9u_qJW1eKR0
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
|
|
@ -16,29 +19,29 @@ class AgilityBossDialogue(player: Player? = null) : DialoguePlugin(player){
|
|||
override fun open(vararg args: Any?): Boolean {
|
||||
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 }
|
||||
} 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
|
||||
}
|
||||
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
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, ",
|
||||
"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",
|
||||
"fall into a pit of spikes. Also, I would advise not",
|
||||
"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++ }
|
||||
|
||||
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 }
|
||||
|
||||
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?
|
||||
//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_ANGRY(7168),
|
||||
CHILD_SIDE_EYE(7169),
|
||||
|
|
|
|||
|
|
@ -261,6 +261,17 @@ public final class Location extends Node {
|
|||
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.
|
||||
* @param first The first location.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue