diff --git a/.gitignore b/.gitignore index 7f6d6899..3cc01092 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,8 @@ dump.hprof /web-api/website/node_modules /content/src/main/kotlin/generated/org/alter/tables /content/src/main/kotlin/org/generated +<<<<<<< HEAD +/data/cfg/gamevals-binary +======= *.dat +>>>>>>> upstream/main diff --git a/content/src/main/kotlin/org/alter/objects/ladder/LadderPlugin.kt b/content/src/main/kotlin/org/alter/objects/ladder/LadderPlugin.kt index 5287184f..acc3cba4 100644 --- a/content/src/main/kotlin/org/alter/objects/ladder/LadderPlugin.kt +++ b/content/src/main/kotlin/org/alter/objects/ladder/LadderPlugin.kt @@ -50,7 +50,23 @@ class LadderPlugin : PluginEvent() { player.moveTo(3210, 9616, 0) } onObjectOption("objects.ladder_from_cellar", "climb-up") { - player.moveTo(3210, 3216, 0) + when(player.tile.regionId) { + 10139 -> { + player.moveTo(2547, 3552, 0) + } + 12950 -> { + player.moveTo(3210, 3216, 0) + } + else -> player.message("Nothing interesting happens.") + } + } + onObjectOption("objects.ladder_outside_to_underground", "climb-down") { + when(player.tile.regionId) { + 10039 -> { + player.moveTo(2548, 9951, 0) + } + else -> player.message("Nothing interesting happens.") + } } } diff --git a/content/src/main/kotlin/org/alter/skills/agility/regularcourses/BarbianOutpostCoursePlugin.kt b/content/src/main/kotlin/org/alter/skills/agility/regularcourses/BarbianOutpostCoursePlugin.kt new file mode 100644 index 00000000..b963e26a --- /dev/null +++ b/content/src/main/kotlin/org/alter/skills/agility/regularcourses/BarbianOutpostCoursePlugin.kt @@ -0,0 +1,302 @@ +package org.alter.skills.agility.regularcourses + +import org.alter.api.HitType +import org.alter.api.Skills +import org.alter.api.ext.filterableMessage +import org.alter.api.ext.hit +import org.alter.game.model.Direction +import org.alter.game.model.ForcedMovement +import org.alter.game.model.Tile +import org.alter.game.model.attr.AttributeKey +import org.alter.game.model.entity.Player +import org.alter.game.model.move.moveTo +import org.alter.game.pluginnew.PluginEvent +import org.alter.game.pluginnew.event.impl.onObjectOption +import org.alter.rscm.RSCM +import org.alter.skills.agility.MarkOfGraceService + +class BarbianOutpostCoursePlugin : PluginEvent() { + + private val MAX_STAGES = 8 + private val BONUS_XP = 46.3 + private val STRENGTH_XP = 41.3 + + private val DROP_CHANCE = 1.0 / 5.0 // 20% + + private val FAIL_ROPE = 1.0 / 3.0 + private val FAIL_LOG = 1.0 / 10.0 + private val FAIL_LEDGE = 1.0 / 7.0 + + private val MARK_SPAWN_TILES = listOf( + Tile(2504, 3545, 1), + Tile(2533, 3555, 0) + ) + private val GraceService = MarkOfGraceService( + spawnTiles = MARK_SPAWN_TILES, + dropChance = DROP_CHANCE + ) + + val BARBARIAN_AGILITY_STAGE = AttributeKey("BarbarianAgilityStage") + private fun Player.getStage(): Int = attr[BARBARIAN_AGILITY_STAGE] ?: 0 + private fun Player.setStage(v: Int) { attr[BARBARIAN_AGILITY_STAGE] = v } + + val BARBARIAN_LAPS = AttributeKey("BarbarianAgilityLaps") + private fun Player.getLaps(): Int = attr[BARBARIAN_LAPS] ?: 0 + private fun Player.setLaps(v: Int) { attr[BARBARIAN_LAPS] = v } + + override fun init() { + + onObjectOption("objects.obstical_ropeswing1", "Swing-on") { + handleObstacle( + player = player, + destination = Tile(2551, 3549, 0), + anim = "sequences.human_ropeswing_long", + duration1 = 5, + duration2 = 250, + angle = Direction.SOUTH.angle, + xp = 22.0, + messageStart = "You grab the rope...", + messageEnd = "... and land safely.", + stage = 1, + + failChance = FAIL_ROPE, + onFail = { p -> + p.queue { + p.filterableMessage("You lose your grip and fall the pit below!") + p.animate("sequences.human_ropeswing_long_miss") + wait(3) + p.moveTo(Tile(2552, 9950, 0)) + wait(1) + p.animate(RSCM.NONE) + p.hit(damage = 2, type = HitType.HIT) + } + } + ) + } + + onObjectOption("objects.barbarian_log_balance1", "walk-across") { + handleObstacle( + player = player, + destination = Tile(2541, 3546, 0), + anim = "sequences.human_walk_logbalance_loop", + duration1 = 250, + duration2 = 250, + angle = Direction.WEST.angle, + xp = 13.7, + messageStart = "You walk carefully across the slippery log...", + messageEnd = "... and make it safely to the other side.", + stage = 2, + + failChance = FAIL_LOG, + onFail = { p -> + p.queue { + p.filterableMessage("You lose your footing and fall off the log!") + p.animate("sequences.human_walk_logbalance_stumble") + wait(1) + p.animate("sequences.human_drowning") + wait(2) + p.moveTo(Tile(2545, 3546, 0)) + wait(1) + p.hit(damage = 3, type = HitType.HIT) + } + } + ) + } + + onObjectOption("objects.agility_obstical_net_barbarian", "climb-over") { + if (player.tile.z !in 3545..3546) return@onObjectOption + handleObstacle( + player = player, + destination = Tile(2537, player.tile.z, 1), + anim = "sequences.human_reachforladder", + simpleMove = true, + xp = 8.2, + messageStart = "You climb up the netting...", + stage = 3 + ) + } + + onObjectOption("objects.balancing_ledge1", "Walk-across") { + handleObstacle( + player = player, + destination = Tile(2532, 3547, 1), + anim = "sequences.human_walk_sidestepl", + duration1 = 5, + duration2 = 250, + angle = Direction.WEST.angle, + xp = 22.0, + messageStart = "You carefully edge across the ledge...", + stage = 4, + + failChance = FAIL_LEDGE, + onFail = { p -> + p.queue { + p.filterableMessage("You lose your balance and fall!") + p.animate("sequences.human_sidestep_fall") + wait(2) + p.moveTo(Tile(2534, 3546, 0)) + wait(1) + p.hit(damage = 2, type = HitType.HIT) + p.animate(RSCM.NONE) + } + } + + ) + } + + onObjectOption("objects.barbarian_laddertop_norim", "Climb-down") { + handleObstacle( + player = player, + destination = Tile(2532, 3546, 0), + anim = "sequences.human_reachforladder", + simpleMove = true, + messageStart = "You climb down the ladder...", + stage = 5 + ) + } + + onObjectOption("objects.castlecrumbly1", "climb-over") { + + when (player.tile.x) { + + 2535, 2536 -> + handleObstacle( + player = player, + destination = Tile(2537, 3553, 0), + anim = "sequences.human_walk_crumbledwall", + simpleMove = true, + xp = 13.7, + messageStart = "You climb over the crumbling wall...", + stage = 6, + ) + + 2538, 2539 -> + handleObstacle( + player = player, + destination = Tile(2540, 3553, 0), + anim = "sequences.human_walk_crumbledwall", + simpleMove = true, + xp = 13.7, + messageStart = "You climb over the crumbling wall...", + stage = 7, + ) + + 2541, 2542 -> + handleObstacle( + player = player, + destination = Tile(2543, 3553, 0), + anim = "sequences.human_walk_crumbledwall", + simpleMove = true, + xp = 13.7, + messageStart = "You climb over the crumbling wall...", + stage = 8, + endStage = true, + ) + } + } + } + + private fun handleObstacle( + player: Player, + destination: Tile, + simpleMove: Boolean = false, + angle: Int? = null, + duration1: Int? = null, + duration2: Int? = null, + anim: String? = null, + xp: Double = 0.0, + messageStart: String? = null, + messageEnd: String? = null, + stage: Int = -1, + endStage: Boolean = false, + + failChance: Double = 0.0, + onFail: ((Player) -> Unit)? = null + ) { + + val doForcedMove = angle != null && duration1 != null && duration2 != null + val current = player.getStage() + + val isNext = current + 1 == stage + val isRepeat = current == stage + + if (isNext) { + player.setStage(stage) + } else if (!isRepeat) { + player.setStage(0) + } + + player.queue { + + if (failChance > 0 && Math.random() < failChance) { + player.setStage(0) + onFail?.invoke(player) + return@queue + } + + messageStart?.let { player.filterableMessage(it) } + anim?.let { player.animate(it) } + + if (doForcedMove) { + val movement = ForcedMovement.of( + src = player.tile, + dst = destination, + clientDuration1 = duration1, + clientDuration2 = duration2, + directionAngle = angle + ) + player.forceMove(this, movement) + wait(1) + player.animate(RSCM.NONE) + + } else if (simpleMove) { + wait(2) + player.moveTo(destination) + wait(1) + player.animate(RSCM.NONE) + } + + wait(1) + + if (xp > 0.0) + player.addXp(Skills.AGILITY, xp) + + messageEnd?.let { player.filterableMessage(it) } + + handleStage(player, stage, endStage) + } + } + + private fun handleStage( + player: Player, + stage: Int, + endStage: Boolean + ) { + val cur = player.getStage() + + if (!endStage && stage >= 0) { + if (stage == cur + 1) { + player.setStage(stage) + } + return + } + + if (endStage) { + val completed = cur == MAX_STAGES + + if (completed) { + player.addXp(Skills.AGILITY, BONUS_XP) + player.addXp(Skills.STRENGTH, STRENGTH_XP) + + val laps = player.getLaps() + 1 + player.setLaps(laps) + + player.filterableMessage("Your Barbarian Agility lap count is: $laps.") + + GraceService.spawnMarkofGrace(player) + } + + player.setStage(0) + } + } +} diff --git a/content/src/main/kotlin/org/alter/skills/agility/regularcourses/GnomeStrongholdCoursePlugin.kt b/content/src/main/kotlin/org/alter/skills/agility/regularcourses/GnomeStrongholdCoursePlugin.kt new file mode 100644 index 00000000..00a04561 --- /dev/null +++ b/content/src/main/kotlin/org/alter/skills/agility/regularcourses/GnomeStrongholdCoursePlugin.kt @@ -0,0 +1,235 @@ +package org.alter.skills.agility.regularcourses + +import org.alter.api.ChatMessageType +import org.alter.api.Skills +import org.alter.api.ext.filterableMessage +import org.alter.api.ext.loopAnim +import org.alter.api.ext.message +import org.alter.api.ext.stopLoopAnim +import org.alter.api.ext.stopWalkAnimOverride +import org.alter.api.ext.walkAnimOverride +import org.alter.game.model.Direction +import org.alter.game.model.ForcedMovement +import org.alter.game.model.Tile +import org.alter.game.model.attr.AttributeKey +import org.alter.game.model.entity.Player +import org.alter.game.model.move.forceStep +import org.alter.game.model.move.moveTo +import org.alter.game.pluginnew.PluginEvent +import org.alter.game.pluginnew.event.impl.onObjectOption +import org.alter.rscm.RSCM +import org.alter.skills.agility.MarkOfGraceService + +class GnomeStrongholdCoursePlugin : PluginEvent() { + + private val MAX_STAGES = 7 + private val BONUS_XP = 50.0 + private val DROP_CHANCE = 1.0 / 3.0 // 1/3 chance per lap completion + + private val MARK_SPAWN_TILES = listOf( + Tile(2471, 3422, 1), + Tile(2474, 3418, 2), + Tile(2488, 3421, 2) + ) + private val GraceService = MarkOfGraceService( + spawnTiles = MARK_SPAWN_TILES, + dropChance = DROP_CHANCE + ) + val GNOME_AGILITY_STAGE = AttributeKey("GnomeAgilityStage") + private fun Player.stage(): Int = attr[GNOME_AGILITY_STAGE] ?: 0 + private fun Player.setStage(v: Int) { attr[GNOME_AGILITY_STAGE] = v } + + val GNOME_LAPS = AttributeKey("GnomeAgilityLaps") + private fun Player.laps(): Int = attr[GNOME_LAPS] ?: 0 + private fun Player.setLaps(v: Int) { attr[GNOME_LAPS] = v } + + + override fun init() { + onObjectOption("objects.gnome_log_balance1", "walk-across") { + val dest = Tile(2474, 3429, 0) + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You walk carefully across the slippery log...") + player.animate("sequences.human_walk_logbalance_ready") + wait(1) + player.loopAnim("sequences.human_walk_logbalance_loop") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.SOUTH.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + wait(1) + player.animate(RSCM.NONE) + player.message(type = ChatMessageType.GAME_MESSAGE, message = "... and make it safely to the other side.") + player.addXp(Skills.AGILITY, 7.5) + player.setStage(1) + } + } + onObjectOption("objects.obstical_net2", "climb-over") { + if(player.tile.x !in 2471..2476) return@onObjectOption + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You climb up the netting...") + player.animate("sequences.human_reachforladder") + wait(2) + player.moveTo(player.tile.x, 3424, 1) + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 10.0) + player.setStage(2) + } + } + + onObjectOption("objects.climbing_branch", "climb") { + val dest = Tile (2473, 3420, 2) + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You climb the tree...") + player.animate("sequences.human_reachforladder") + wait(2) + player.moveTo(dest) + wait(1) + player.animate(RSCM.NONE) + player.message(type = ChatMessageType.GAME_MESSAGE, message = "... to the platform above.") + player.addXp(Skills.AGILITY, 6.5) + player.setStage(2) + } + } + onObjectOption("objects.balancing_rope", "walk-on") { + val dest = Tile(2483, 3420, 2) + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You carefully cross the tighrope.") + player.animate("sequences.human_walk_logbalance_ready") + wait(1) + player.loopAnim("sequences.human_walk_logbalance_loop") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.EAST.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 10.0) + player.setStage(4) + } + } + onObjectOption("objects.climbing_tree", "climb-down") { + val dest = Tile (2487, 3420, 0) + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You climb the tree...") + player.animate("sequences.human_reachforladder") + wait(2) + player.moveTo(dest) + wait(1) + player.animate(RSCM.NONE) + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You land on the ground.") + player.addXp(Skills.AGILITY, 6.5) + player.setStage(5) + } + } + onObjectOption("objects.climbing_tree2", "climb-down") { + val dest = Tile (2487, 3420, 0) + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You climb the tree...") + player.animate("sequences.human_reachforladder") + wait(2) + player.moveTo(dest) + wait(1) + player.animate(RSCM.NONE) + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You land on the ground.") + player.addXp(Skills.AGILITY, 6.5) + player.setStage(5) + } + } + onObjectOption("objects.obstical_net3", "climb-over") { + if(player.tile.x !in 2483..2488) return@onObjectOption + + player.queue { + player.message(type = ChatMessageType.GAME_MESSAGE, message = "You climb up the netting...") + player.animate("sequences.human_reachforladder") + wait(2) + player.moveTo(player.tile.x, 3428, 0) + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 10.0) + player.setStage(6) + } + } + onObjectOption("objects.obstical_pipe3_1", "squeeze-through") { + val dest = Tile(2484, 3437, 0) + + player.queue { + player.animate("sequences.human_pipesqueeze_ready") + wait(1) + player.loopAnim("sequences.human_pipesqueeze") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.NORTH.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + player.animate("sequences.human_pipeunsqueeze") + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 10.0) + + if (player.stage() == MAX_STAGES) { + val laps = player.laps() + 1 + player.setLaps(laps) + player.filterableMessage("Your Gnome Stronghold Agility lap count is: $laps.") + player.addXp(Skills.AGILITY, BONUS_XP) + + GraceService.spawnMarkofGrace(player) + } + player.setStage(0) + } + } + onObjectOption("objects.obstical_pipe3_2", "squeeze-through") { + val dest = Tile(2487, 3437, 0) + + player.queue { + player.animate("sequences.human_pipesqueeze_ready") + wait(1) + player.loopAnim("sequences.human_pipesqueeze") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.NORTH.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + player.animate("sequences.human_pipeunsqueeze") + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 10.0) + + if (player.stage() == MAX_STAGES) { + val laps = player.laps() + 1 + player.setLaps(laps) + player.filterableMessage("Your Gnome Stronghold Agility lap count is: $laps.") + player.addXp(Skills.AGILITY, BONUS_XP) + + GraceService.spawnMarkofGrace(player) + } + player.setStage(0) + } + } + } + +} \ No newline at end of file diff --git a/content/src/main/kotlin/org/alter/skills/agility/regularcourses/ShayzienCoursePlugin.kt b/content/src/main/kotlin/org/alter/skills/agility/regularcourses/ShayzienCoursePlugin.kt new file mode 100644 index 00000000..35618953 --- /dev/null +++ b/content/src/main/kotlin/org/alter/skills/agility/regularcourses/ShayzienCoursePlugin.kt @@ -0,0 +1,375 @@ +package org.alter.skills.agility.regularcourses + +import org.alter.api.ChatMessageType +import org.alter.api.Skills +import org.alter.api.ext.filterableMessage +import org.alter.api.ext.loopAnim +import org.alter.api.ext.stopLoopAnim +import org.alter.api.ext.stopWalkAnimOverride +import org.alter.api.ext.walkAnimOverride +import org.alter.game.model.Direction +import org.alter.game.model.ForcedMovement +import org.alter.game.model.Tile +import org.alter.game.model.attr.AttributeKey +import org.alter.game.model.entity.Player +import org.alter.game.model.move.forceStep +import org.alter.game.model.move.moveTo +import org.alter.game.pluginnew.PluginEvent +import org.alter.game.pluginnew.event.impl.onObjectOption +import org.alter.rscm.RSCM +import org.alter.skills.agility.MarkOfGraceService + +class ShayzienCoursePlugin : PluginEvent() { + + private val MAX_STAGES = 7 + private val ADVANCED_STAGES = 8 + private val DROP_CHANCE = 1.0 / 10.0 + + private val MARK_SPAWN_TILES = listOf( + Tile(1525, 3636, 2) + ) + + private val GraceService = MarkOfGraceService( + spawnTiles = MARK_SPAWN_TILES, + dropChance = DROP_CHANCE + ) + + val STAGE = AttributeKey("ShayzienAgilityStage") + fun Player.stage() = attr[STAGE] ?: 0 + fun Player.setStage(v: Int) { attr[STAGE] = v } + + val LAPS = AttributeKey("ShayzienAgilityLaps") + fun Player.laps() = attr[LAPS] ?: 0 + fun Player.setLaps(v: Int) { attr[LAPS] = v } + + val ADVANCEDSTAGE = AttributeKey("AdvancedShayzienAgilityStage") + fun Player.advancedstage() = attr[ADVANCEDSTAGE] ?: 0 + fun Player.setAdvancedStage(v: Int) { attr[ADVANCEDSTAGE] = v } + + val ADVANCEDLAPS = AttributeKey("AdvancedShayzienAgilityLaps") + fun Player.advancedlaps() = attr[ADVANCEDLAPS] ?: 0 + fun Player.setAdvancedLaps(v: Int) { attr[ADVANCEDLAPS] = v } + + override fun init() { + + onObjectOption("objects.shayzien_agility_both_start_ladder", "climb") { + val dest = Tile(1554, 3632, 3) + + player.queue { + player.animate("sequences.human_reachforladder") + + wait(2) + player.moveTo(dest) + wait(1) + player.animate(RSCM.NONE) + + player.addXp(Skills.AGILITY, 5.5) + player.setStage(1) + player.setAdvancedStage(1) + } + } + + onObjectOption("objects.shayzien_agility_both_rope_climb", "Climb") { + val realDest = Tile(1541, 3633, 2) + val fakeDest = Tile(realDest.x, realDest.z, player.tile.height) + + player.queue { + player.walkAnimOverride("sequences.human_monkeybars_walk") + player.animate("sequences.human_monkeybars_on") + player.avatar.extendedInfo.setTempMoveSpeed(1) + player.forceStep(fakeDest) + wait (13) + player.stopWalkAnimOverride() + player.animate("sequences.human_monkeybars_off") + wait(1) + player.moveTo(realDest) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 8.0) + player.setStage(2) + player.setAdvancedStage(2) + } + } + + + onObjectOption("objects.shayzien_agility_both_rope_walk", "Cross") { + val dest = Tile(1528, 3633, 2) + + player.queue { + player.animate("sequences.human_walk_logbalance_ready") + wait(1) + player.loopAnim("sequences.human_walk_logbalance_loop") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.WEST.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 9.0) + player.setStage(3) + player.setAdvancedStage(3) + } + } + + onObjectOption("objects.shayzien_agility_low_bar_climb", "Climb") { + val dest = Tile(1523, 3643, 3) + + player.queue { + player.walkAnimOverride("sequences.human_monkeybars_walk") + player.animate("sequences.human_monkeybars_on") + wait(1) + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.NORTH.angle + ) + player.forceMove(this, fm) + player.stopWalkAnimOverride() + player.animate("sequences.human_monkeybars_off") + wait(1) + player.moveTo(dest) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 7.0) + player.setStage(4) + player.setAdvancedStage(0) + } + } + + onObjectOption("objects.shayzien_agility_low_rope_walk_1", "Cross") { + val dest = Tile(1538, 3644, 3) + + player.queue { + player.animate("sequences.human_walk_logbalance_ready") + wait(1) + player.loopAnim("sequences.human_walk_logbalance_loop") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.EAST.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + wait(1) + player.animate("sequences.agility_shortcut_wall_jumpdown") + wait(1) + player.moveTo(1539, 3644, 2) + player.animate("sequences.agility_shortcut_wall_jumpdown2") + wait(2) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 9.0) + player.setStage(5) + } + } + + onObjectOption("objects.shayzien_agility_low_rope_walk_2", "Cross") { + val dest = Tile(1552, 3644, 2) + + player.queue { + player.animate("sequences.human_walk_logbalance_ready") + wait(1) + player.loopAnim("sequences.human_walk_logbalance_loop") + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.EAST.angle + ) + player.forceMove(this, fm) + player.stopLoopAnim() + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 9.0) + player.filterableMessage("... and land safely.") + player.setStage(6) + } + } + + onObjectOption("objects.shayzien_agility_low_end_jump", "Jump") { + val dest = Tile(1554, 3640, 0) + + player.queue { + player.faceDirection(Direction.NORTH) + player.animate("sequences.agility_shortcut_wall_jumpdown") + wait(2) + player.moveTo(dest) + player.animate("sequences.agility_shortcut_wall_jumpdown2") + wait(1) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 106.0) + + // LAP CHECK + if (player.stage() == MAX_STAGES) { + val laps = player.laps() + 1 + player.setLaps(laps) + player.filterableMessage("Your Shayzien Basic Agility Cource lap count is: $laps.") + + GraceService.spawnMarkofGrace(player) + } + + player.setStage(0) + } + } + //Advanced + onObjectOption("objects.shayzien_agility_up_swing_jump_1", "Grapple") { + val dest = Tile(1511, 3637, 2) + + //TODO: Add crossbow and mithrill grapple requirements + + player.queue { + player.filterableMessage("You fire your grapple at the pylon...") + player.animate("sequences.dorgesh_xbow_swing", 6) + player.graphic("spotanims.dorgesh_grapple_spot",82, 6) + wait(1) + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.WEST.angle + ) + player.forceMove(this, fm) + player.moveTo(dest) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 23.0) + player.setStage(0) + player.setAdvancedStage(4) + } + } + onObjectOption("objects.shayzien_agility_up_jump_platform_1", "Jump") { + val dest = Tile(1510, 3630, 2) + + + player.queue { + player.loopAnim("sequences.human_walk_b") + player.lock + player.unlock() + player.moveTo(1510, 3636, 2) + wait(3) + player.stopLoopAnim() + player.loopAnim("sequences.ic_running") + player.moveTo(1510, 3635, 2) + wait(1) + player.stopLoopAnim() + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 5, + clientDuration2 = 250, + directionAngle = Direction.SOUTH.angle + ) + player.animate("sequences.human_jump_hurdle") + player.forceMove(this, fm) + player.moveTo(dest) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 18.0) + player.setAdvancedStage(5) + } + } + onObjectOption("objects.shayzien_agility_up_jump_platform_2", "Jump") { + val dest1 = Tile(1510, 3625, 2) + val dest2 = Tile(1510, 3622, 2) + val dest3 = Tile(1510, 3620, 2) + + + player.queue { + val fm1 = ForcedMovement.of( + src = player.tile, + dst = dest1, + clientDuration1 = 5, + clientDuration2 = 50, + directionAngle = Direction.SOUTH.angle + ) + player.animate("sequences.human_jump_hurdle") + player.forceMove(this, fm1) + wait(2) + val fm2 = ForcedMovement.of( + src = player.tile, + dst = dest2, + clientDuration1 = 5, + clientDuration2 = 50, + directionAngle = Direction.SOUTH.angle + ) + player.animate("sequences.human_jump_hurdle") + player.forceMove(this, fm2) + val fm3 = ForcedMovement.of( + src = player.tile, + dst = dest3, + clientDuration1 = 5, + clientDuration2 = 50, + directionAngle = Direction.SOUTH.angle + ) + player.animate("sequences.human_jump_hurdle") + player.forceMove(this, fm3) + wait(5) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 21.0) + player.setAdvancedStage(6) + } + } + onObjectOption("objects.shayzien_agility_up_swing_jump_2", "Grapple") { + val dest = Tile(1521, 3619, 2) + + //TODO: Add crossbow and mithrill grapple requirements + + player.queue { + player.filterableMessage("You fire your grapple at the pylon...") + player.animate("sequences.dorgesh_xbow_swing", 6) + player.graphic("spotanims.dorgesh_grapple_spot",82, 6) + wait(2) + val fm = ForcedMovement.of( + src = player.tile, + dst = dest, + clientDuration1 = 0, + clientDuration2 = 150, + directionAngle = Direction.EAST.angle + ) + player.forceMove(this, fm) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 23.0) + player.setStage(0) + player.setAdvancedStage(7) + } + } + onObjectOption("objects.shayzien_agility_up_end_jump", "Slide") { + val realDest = Tile(1522, 3625, 0) + val fakeDest = Tile(realDest.x, realDest.z, player.tile.height) + + player.queue { + player.faceDirection(Direction.NORTH) + player.animate("sequences.human_monkeybars_on") + wait(2) + val fm = ForcedMovement.of( + src = player.tile, + dst = fakeDest, + clientDuration1 = 0, + clientDuration2 = 100, + directionAngle = Direction.NORTH.angle + ) + wait(1) + player.animate("sequences.human_monkeybars_off") + player.moveTo(realDest) + player.animate(RSCM.NONE) + player.addXp(Skills.AGILITY, 400.0) + + if (player.stage() == ADVANCED_STAGES) { + val advancedlaps = player.advancedlaps() + 1 + player.setAdvancedLaps(advancedlaps) + player.filterableMessage("Your Shayzien Advanced Agility Cource lap count is: $advancedlaps.") + + GraceService.spawnMarkofGrace(player) + } + + player.setStage(0) + } + } + } +} diff --git a/content/src/main/kotlin/org/alter/skills/agility/rooftopcourses/DraynorRooftopCoursePlugin.kt b/content/src/main/kotlin/org/alter/skills/agility/rooftopcourses/DraynorRooftopCoursePlugin.kt new file mode 100644 index 00000000..b49953cc --- /dev/null +++ b/content/src/main/kotlin/org/alter/skills/agility/rooftopcourses/DraynorRooftopCoursePlugin.kt @@ -0,0 +1,238 @@ +package org.alter.skills.agility.rooftopcourses + +import org.alter.api.Skills +import org.alter.api.ext.filterableMessage +import org.alter.game.model.Direction +import org.alter.game.model.ForcedMovement +import org.alter.game.model.Tile +import org.alter.game.model.attr.AttributeKey +import org.alter.game.model.entity.Player +import org.alter.game.model.move.moveTo +import org.alter.game.pluginnew.PluginEvent +import org.alter.game.pluginnew.event.impl.onObjectOption +import org.alter.rscm.RSCM +import org.alter.skills.agility.MarkOfGraceService + +class DraynorRooftopCoursePlugin : PluginEvent() { + + private val MAX_STAGES = 7 + private val BONUS_XP = 120.0 + private val DROP_CHANCE = 0.15 // 15% chance per obstacle + + private val MARK_SPAWN_TILES = listOf( + Tile(3088, 3275, 3), + ) + private val GraceService = MarkOfGraceService( + spawnTiles = MARK_SPAWN_TILES, + dropChance = DROP_CHANCE + ) + val DRAYNOR_AGILITY_STAGE = AttributeKey("DraynorAgilityStage") + private fun Player.getStage(): Int = attr[DRAYNOR_AGILITY_STAGE] ?: 0 + private fun Player.setStage(v: Int) { attr[DRAYNOR_AGILITY_STAGE] = v } + + val DRAYNOR_LAPS = AttributeKey("DraynorAgilityLaps") + private fun Player.getLaps(): Int = attr[DRAYNOR_LAPS] ?: 0 + private fun Player.setLaps(v: Int) { attr[DRAYNOR_LAPS] = v } + + + override fun init() { + + //Rough Wall (Climb up) + onObjectOption("objects.rooftops_draynor_wallclimb", "climb") { + handleObstacle( + player = player, + destination = Tile(3103, 3279, 3), + anim = "sequences.human_reachforladder", + simpleMove = true, + xp = 8.0, + messageStart = "You climb up the rough wall...", + messageEnd = "...and reach the top.", + stage = 1 + ) + } + + //Tightrope 1 + onObjectOption("objects.rooftops_draynor_tightrope_1", "cross") { + handleObstacle( + player = player, + destination = Tile(3090, 3277, 3), + anim = "sequences.human_walk_logbalance_loop", + duration1 = 5, + duration2 = 250, + angle = Direction.WEST.angle, + xp = 8.0, + messageStart = "You carefully cross the tightrope.", + stage = 2 + ) + } + + //Tightrope 2 + onObjectOption("objects.rooftops_draynor_tightrope_2", "cross") { + handleObstacle( + player = player, + destination = Tile(3092, 3267, 3), + anim = "sequences.human_walk_logbalance_loop", + duration1 = 5, + duration2 = 250, + angle = Direction.SOUTH.angle, + xp = 8.0, + messageStart = "You carefully cross the tightrope.", + stage = 3 + ) + } + + //Narrow Wall (Balance) + onObjectOption("objects.rooftops_draynor_wallcrossing", "balance") { + handleObstacle( + player = player, + destination = Tile(3088, 3261, 3), + anim = "sequences.human_walk_logbalance_loop", + duration1 = 5, + duration2 = 250, + angle = Direction.SOUTH.angle, + xp = 8.0, + messageStart = "You balance across the narrow wall.", + stage = 4 + ) + } + + //Wall (Jump) + onObjectOption("objects.rooftops_draynor_wallscramble", "jump-up") { + handleObstacle( + player = player, + destination = Tile(3088, 3254, 3), + anim = "sequences.agility_shortcut_wall_jump", + simpleMove = true, + xp = 8.0, + messageStart = "You jump across the wall.", + stage = 5 + ) + } + + //Gap (Jump down) + onObjectOption("objects.rooftops_draynor_leapdown", "jump") { + handleObstacle( + player = player, + destination = Tile(3096, 3256, 3), + anim = "sequences.agility_shortcut_wall_jumpdown", + simpleMove = true, + xp = 8.0, + messageStart = "You jump down from the gap.", + messageEnd = "You land safely on the ground.", + stage = 6 + ) + } + + //Crate (Jump) — EndStage + onObjectOption("objects.rooftops_draynor_crate", "climb-down") { + handleObstacle( + player = player, + destination = Tile(3103, 3261, 0), + anim = "sequences.human_jump_hurdle", + simpleMove = true, + xp = 8.0, + messageStart = "You jump onto the crate.", + messageEnd = "You complete the course!", + stage = 7, + endStage = true + ) + } + } + + private fun handleObstacle( + player: Player, + destination: Tile, + simpleMove: Boolean = false, + angle: Int? = null, + duration1: Int? = null, + duration2: Int? = null, + anim: String? = null, + xp: Double = 0.0, + messageStart: String? = null, + messageEnd: String? = null, + stage: Int = -1, + endStage: Boolean = false + ) { + + val doForcedMove = angle != null && duration1 != null && duration2 != null + + val current = player.getStage() + + val isNext = current + 1 == stage + val isRepeat = current == stage + + if (isNext) { + player.setStage(stage) + } else if (!isRepeat) { + player.setStage(0) + } + + + + player.queue { + + messageStart?.let { player.filterableMessage(it) } + anim?.let { player.animate(it) } + + if (doForcedMove) { + val movement = ForcedMovement.Companion.of( + src = player.tile, + dst = destination, + clientDuration1 = duration1, + clientDuration2 = duration2, + directionAngle = angle + ) + player.forceMove(this, movement) + wait(1) + player.animate(RSCM.NONE) + } + else if (simpleMove) { + wait(2) + player.moveTo(destination) + wait(1) + player.animate(RSCM.NONE) + } + + wait(1) + if (xp > 0.0) player.addXp(Skills.AGILITY, xp) + messageEnd?.let { player.filterableMessage(it) } + + handleStage(player, stage, endStage) + } + } + + private fun handleStage( + player: Player, + stage: Int, + endStage: Boolean + ) { + val cur = player.getStage() + + if (!endStage && stage >= 0) { + if (stage == cur + 1) { + player.setStage(stage) + } + return + } + + if (endStage) { + val completed = cur == MAX_STAGES + + if (completed) { + player.addXp(Skills.AGILITY, BONUS_XP) + + val laps = player.getLaps() + 1 + player.setLaps(laps) + + player.filterableMessage("Your Draynor Rooftop Agility lap count is: $laps.") + + GraceService.spawnMarkofGrace(player) + } + + player.setStage(0) + return + + } + } + +} \ No newline at end of file diff --git a/content/src/main/kotlin/org/alter/skills/agility/services/MarkOfGraceService.kt b/content/src/main/kotlin/org/alter/skills/agility/services/MarkOfGraceService.kt new file mode 100644 index 00000000..c468a441 --- /dev/null +++ b/content/src/main/kotlin/org/alter/skills/agility/services/MarkOfGraceService.kt @@ -0,0 +1,59 @@ +package org.alter.skills.agility + +import org.alter.api.ext.filterableMessage +import org.alter.game.model.Tile +import org.alter.game.model.entity.GroundItem +import org.alter.game.model.entity.Player +import org.alter.rscm.RSCM.asRSCM +import org.alter.game.model.EntityType + +class MarkOfGraceService( + private val spawnTiles: List, + private val dropChance: Double, + private val itemName: String = "items.grace", + private val despawnSeconds: Int = 600 +) { + + private val itemId = itemName.asRSCM() + + fun hasMarksSpawned(player: Player, tile: Tile = player.tile): Boolean { + val chunk = player.world.chunks.getOrCreate(tile) + + return chunk.getEntities(tile, EntityType.GROUND_ITEM).any { + it.item == itemId && it.tile == tile && it.isOwnedBy(player) + } + } + + fun spawnMarkofGrace(player: Player) { + if (Math.random() > dropChance) return + + val tile = spawnTiles.random() + + val chunk = player.world.chunks.getOrCreate(tile) + + val existing = if (hasMarksSpawned(player, tile)) { + chunk.getEntities(tile, EntityType.GROUND_ITEM).firstOrNull { + it.item == itemId && it.tile == tile && it.isOwnedBy(player) + } + } else { + null + } + + if (existing != null) { + existing.amount += 1 + existing.timeUntilDespawn = despawnSeconds + } else { + val mark = GroundItem( + item = itemId, + amount = 1, + tile = tile, + owner = player + ) + mark.timeUntilDespawn = despawnSeconds + player.world.spawn(mark) + } + + player.filterableMessage("A Mark of Grace appears.") + } +} + diff --git a/game-api/src/main/kotlin/org/alter/api/ext/PlayerExt.kt b/game-api/src/main/kotlin/org/alter/api/ext/PlayerExt.kt index 44e72063..a00d0013 100644 --- a/game-api/src/main/kotlin/org/alter/api/ext/PlayerExt.kt +++ b/game-api/src/main/kotlin/org/alter/api/ext/PlayerExt.kt @@ -1,6 +1,7 @@ package org.alter.api.ext import dev.openrune.ServerCacheManager +import dev.openrune.ServerCacheManager.getAnim import dev.openrune.ServerCacheManager.getItem import gg.rsmod.util.BitManipulation import net.rsprot.protocol.game.outgoing.interfaces.* @@ -1073,3 +1074,14 @@ fun Player.format_bonus_with_sign(value: Int): String = if (value < 0) value.toS */ val Player.playtime: Int get() = attr[PLAYTIME_ATTR] ?: 0 + + +fun Player.walkAnimOverride(animId: String) { + walkAnimOverride = animId.asRSCM() + org.alter.game.info.PlayerInfo(this).syncAppearance() +} + +fun Player.stopWalkAnimOverride() { + walkAnimOverride = null + org.alter.game.info.PlayerInfo(this).syncAppearance() +} \ No newline at end of file diff --git a/game-plugins/src/main/kotlin/org/alter/plugins/content/commands/commands/MarksPlugin.kt b/game-plugins/src/main/kotlin/org/alter/plugins/content/commands/commands/MarksPlugin.kt new file mode 100644 index 00000000..f184be12 --- /dev/null +++ b/game-plugins/src/main/kotlin/org/alter/plugins/content/commands/commands/MarksPlugin.kt @@ -0,0 +1,50 @@ +package org.alter.plugins.content.commands.commands + +import com.sun.management.HotSpotDiagnosticMXBean +import org.alter.api.ext.player +import org.alter.game.Server +import org.alter.game.model.Tile +import org.alter.game.model.World +import org.alter.game.model.entity.GroundItem +import org.alter.game.model.move.moveTo +import org.alter.game.plugin.KotlinPlugin +import org.alter.game.plugin.PluginRepository +import org.alter.rscm.RSCM.getRSCM +import java.lang.management.ManagementFactory +import java.nio.file.Paths + +/** + * @author CloudS3c 12/30/2024 + */ +class MarksPlugin( + r: PluginRepository, + world: World, + server: Server +) : KotlinPlugin(r, world, server) { + + + private val MARK_SPAWN_TILES = listOf( + Tile(2471, 3422, 1), + Tile(2474, 3418, 2), + Tile(2488, 3421, 2) + ) + + init { + onCommand("m") { + player.world.spawn( + GroundItem( + item = 11849, + amount =1, + tile = Tile(2474,3418,2), + owner =player, + )) + } + onCommand("m2") { + player.moveTo(Tile(2474,3418,2)) + } + onCommand("m3") { + player.moveTo(Tile(2474,3418,0)) + } + + } +} diff --git a/game-server/src/main/kotlin/org/alter/game/info/PlayerInfo.kt b/game-server/src/main/kotlin/org/alter/game/info/PlayerInfo.kt index 136303b3..ddce3153 100644 --- a/game-server/src/main/kotlin/org/alter/game/info/PlayerInfo.kt +++ b/game-server/src/main/kotlin/org/alter/game/info/PlayerInfo.kt @@ -14,6 +14,7 @@ class PlayerInfo(var player: Player) { val DEFAULT_ANIM_SET = AnimationSet(readyAnim = 808, turnAnim = 823, walkAnim = 819, walkAnimBack = 820, walkAnimLeft = 821, walkAnimRight = 822, runAnim = 824) var animSequance = DEFAULT_ANIM_SET + val walkAnimOverride = player.walkAnimOverride; fun syncAppearance() { if (player.getTransmogId() == -1) { @@ -76,15 +77,29 @@ class PlayerInfo(var player: Player) { } fun syncAnimationSet() { - info.setBaseAnimationSet( - readyAnim = animSequance.readyAnim, - turnAnim = animSequance.turnAnim, - walkAnim = animSequance.walkAnim, - walkAnimBack = animSequance.walkAnimBack, - walkAnimLeft = animSequance.walkAnimLeft, - walkAnimRight = animSequance.walkAnimRight, - runAnim = animSequance.runAnim, - ) + if(walkAnimOverride != null) { + player.writeMessage("Overriding walk with anim $walkAnimOverride") + val plusOne = walkAnimOverride + 1 + info.setBaseAnimationSet( + readyAnim = plusOne, + turnAnim = plusOne, + walkAnim = walkAnimOverride, + walkAnimBack = plusOne, + walkAnimLeft = plusOne, + walkAnimRight = plusOne, + runAnim = -1, + ) + } else { + info.setBaseAnimationSet( + readyAnim = animSequance.readyAnim, + turnAnim = animSequance.turnAnim, + walkAnim = animSequance.walkAnim, + walkAnimBack = animSequance.walkAnimBack, + walkAnimLeft = animSequance.walkAnimLeft, + walkAnimRight = animSequance.walkAnimRight, + runAnim = animSequance.runAnim, + ) + } } fun setSequence(id: Int, delay: Int) { diff --git a/game-server/src/main/kotlin/org/alter/game/model/World.kt b/game-server/src/main/kotlin/org/alter/game/model/World.kt index 31a55d74..06b4760f 100644 --- a/game-server/src/main/kotlin/org/alter/game/model/World.kt +++ b/game-server/src/main/kotlin/org/alter/game/model/World.kt @@ -208,7 +208,7 @@ class World(val gameContext: GameContext, val devContext: DevContext) { * not use [ChunkSet]s to iterate through this as it takes quite a bit of * time to do so every cycle. */ - private val groundItems = ObjectArrayList() + val groundItems = ObjectArrayList() /** * Any ground item that should be spawned in the future. For example, when diff --git a/game-server/src/main/kotlin/org/alter/game/model/attr/Attributes.kt b/game-server/src/main/kotlin/org/alter/game/model/attr/Attributes.kt index b34ca376..f8351579 100644 --- a/game-server/src/main/kotlin/org/alter/game/model/attr/Attributes.kt +++ b/game-server/src/main/kotlin/org/alter/game/model/attr/Attributes.kt @@ -1,10 +1,12 @@ package org.alter.game.model.attr +import org.alter.game.model.Tile import org.alter.game.model.container.ItemTransaction import org.alter.game.model.entity.* import org.alter.game.model.item.Item import org.alter.game.model.shop.Shop import java.lang.ref.WeakReference +import java.util.UUID /** * A decoupled file that holds AttributeKeys that require read-access from our diff --git a/game-server/src/main/kotlin/org/alter/game/model/entity/GroundItem.kt b/game-server/src/main/kotlin/org/alter/game/model/entity/GroundItem.kt index 58542f61..21e248af 100644 --- a/game-server/src/main/kotlin/org/alter/game/model/entity/GroundItem.kt +++ b/game-server/src/main/kotlin/org/alter/game/model/entity/GroundItem.kt @@ -7,6 +7,9 @@ import org.alter.game.model.Tile import org.alter.game.model.World import org.alter.game.model.item.Item import org.alter.game.model.item.ItemAttribute +import org.alter.rscm.RSCM +import org.alter.rscm.RSCM.asRSCM +import org.alter.rscm.RSCMType import java.util.* /** @@ -19,11 +22,20 @@ import java.util.* * * @author Tom */ -class GroundItem private constructor(val item: Int, var amount: Int, internal var ownerUID: PlayerUID?) : Entity() { +class GroundItem(val item: Int, var amount: Int, internal var ownerUID: PlayerUID?) : Entity() { constructor(item: Int, amount: Int, tile: Tile, owner: Player? = null) : this(item, amount, owner?.uid) { this.tile = tile } + constructor(item: String, amount: Int, ownerUID: PlayerUID?) : this(item.asRSCM(), amount, ownerUID) { + RSCM.requireRSCM(RSCMType.OBJTYPES, item) + } + + constructor(item: String, amount: Int, tile: Tile, owner: Player? = null) : this(item.asRSCM(), amount, owner?.uid) { + RSCM.requireRSCM(RSCMType.OBJTYPES, item) + this.tile = tile + } + /** * @TODO Need to implement this. * * | Id | Ownership Type | diff --git a/game-server/src/main/kotlin/org/alter/game/model/entity/Player.kt b/game-server/src/main/kotlin/org/alter/game/model/entity/Player.kt index 6fea2a89..7bb452d6 100644 --- a/game-server/src/main/kotlin/org/alter/game/model/entity/Player.kt +++ b/game-server/src/main/kotlin/org/alter/game/model/entity/Player.kt @@ -178,6 +178,7 @@ open class Player(world: World) : Pawn(world) { var gameMode = 0 var xpRate = 1.0 + var walkAnimOverride: Int? = null /** * The last cycle that this client has received the MAP_BUILD_COMPLETE diff --git a/game-server/src/main/kotlin/org/alter/game/model/move/MovementQueue.kt b/game-server/src/main/kotlin/org/alter/game/model/move/MovementQueue.kt index 22953e5e..30217f6f 100644 --- a/game-server/src/main/kotlin/org/alter/game/model/move/MovementQueue.kt +++ b/game-server/src/main/kotlin/org/alter/game/model/move/MovementQueue.kt @@ -42,9 +42,10 @@ class MovementQueue(val pawn: Pawn) { fun addStep( step: Tile, type: StepType, + clip: Boolean = true ) { val current = if (steps.any()) steps.peekLast().tile else pawn.tile - addStep(current, step, type) + addStep(current, step, type, clip) } /** @@ -107,7 +108,7 @@ class MovementQueue(val pawn: Pawn) { var runDirection: Direction? = null walkDirection = Direction.between(tile, next.tile) if (walkDirection != Direction.NONE && - (pawn.world.canTraverse(tile, walkDirection, pawn)) + (!next.clip || pawn.world.canTraverse(tile, walkDirection, pawn)) ) { tile = next.tile pawn.lastFacingDirection = walkDirection @@ -121,7 +122,7 @@ class MovementQueue(val pawn: Pawn) { next = steps.poll() if (next != null) { runDirection = Direction.between(tile, next.tile) - if (pawn.world.canTraverse(tile, runDirection, pawn)) { + if (!next.clip || pawn.world.canTraverse(tile, runDirection, pawn)) { tile = next.tile pawn.lastFacingDirection = runDirection } else { @@ -151,6 +152,7 @@ class MovementQueue(val pawn: Pawn) { current: Tile, next: Tile, type: StepType, + clip: Boolean = true ) { var dx = next.x - current.x var dy = next.z - current.z @@ -170,13 +172,13 @@ class MovementQueue(val pawn: Pawn) { } val step = next.transform(-dx, -dy) - steps.add(Step(step, type)) + steps.add(Step(step, type, clip)) } } data class StepDirection(val walkDirection: Direction?, val runDirection: Direction?) - data class Step(val tile: Tile, val type: StepType) + data class Step(val tile: Tile, val type: StepType, val clip: Boolean) enum class StepType { NORMAL, diff --git a/game-server/src/main/kotlin/org/alter/game/model/move/PawnMoveExt.kt b/game-server/src/main/kotlin/org/alter/game/model/move/PawnMoveExt.kt index 29738bfd..6e5bd42e 100644 --- a/game-server/src/main/kotlin/org/alter/game/model/move/PawnMoveExt.kt +++ b/game-server/src/main/kotlin/org/alter/game/model/move/PawnMoveExt.kt @@ -153,4 +153,9 @@ fun Pawn.walkTo( } } -fun Pawn.hasMoveDestination(): Boolean = movementQueue.hasDestination() \ No newline at end of file +fun Pawn.hasMoveDestination(): Boolean = movementQueue.hasDestination() + +fun Pawn.forceStep(dest: Tile) { + movementQueue.clear() + movementQueue.addStep(dest, StepType.FORCED_WALK, false) +} \ No newline at end of file