diff --git a/data/area/misthalin/lumbridge/lumbridge.teles.toml b/data/area/misthalin/lumbridge/lumbridge.teles.toml index d15e3de5ad..fd0d470ae2 100644 --- a/data/area/misthalin/lumbridge/lumbridge.teles.toml +++ b/data/area/misthalin/lumbridge/lumbridge.teles.toml @@ -4,7 +4,7 @@ option = "Enter" tile = { x = 3184, y = 3164 } to = { x = 3494, y = 4832 } -[36768] +[lumbridge_tower_ladder_south] option = "Climb-up" tile = { x = 3198, y = 3219 } delta = { level = 1 } @@ -90,7 +90,7 @@ option = "Climb-down" tile = { x = 3207, y = 3223, level = 3 } delta = { y = 2, level = -1 } -[36768] +[lumbridge_tower_ladder_south] option = "Climb-up" tile = { x = 3229, y = 3213 } delta = { level = 1 } @@ -110,7 +110,7 @@ option = "Climb-down" tile = { x = 3229, y = 3213, level = 2 } delta = { level = -1 } -[36768] +[lumbridge_tower_ladder_south] option = "Climb-up" tile = { x = 3229, y = 3224 } delta = { level = 1 } diff --git a/data/area/misthalin/tutorial_island/tutorial_island.teles.toml b/data/area/misthalin/tutorial_island/tutorial_island.teles.toml index a708faf89f..71644fafb2 100644 --- a/data/area/misthalin/tutorial_island/tutorial_island.teles.toml +++ b/data/area/misthalin/tutorial_island/tutorial_island.teles.toml @@ -4,7 +4,7 @@ option = "Climb-up" tile = { x = 3043, y = 10328 } delta = { y = -6400 } -[36768] +[lumbridge_tower_ladder_south] option = "Climb-up" tile = { x = 3126, y = 3124, level = 1 } delta = { level = 1 } @@ -72,7 +72,7 @@ option = "Climb-up" tile = { x = 3088, y = 9971 } to = { x = 3089, y = 3571 } -[36768] +[lumbridge_tower_ladder_south] option = "Climb-up" tile = { x = 3139, y = 3078 } delta = { level = 1 } diff --git a/data/bot/lumbridge.nav-edges.toml b/data/bot/lumbridge.nav-edges.toml index 51371e3510..e46c615236 100644 --- a/data/bot/lumbridge.nav-edges.toml +++ b/data/bot/lumbridge.nav-edges.toml @@ -27,7 +27,7 @@ edges = [ { from = { x = 3184, y = 3225 }, to = { x = 3168, y = 3221 } }, # lumbridge_castle_yew_trees_to_yew_trees_west { from = { x = 3184, y = 3225 }, to = { x = 3195, y = 3236 } }, # lumbridge_castle_yew_trees_to_tree_patch { from = { x = 3226, y = 3214 }, to = { x = 3227, y = 3214 }, cost = 3, actions = [{ object = { option = "Open", id = "door_627_closed", x = 3226, y = 3214, success = { object = { id = "door_627_opened", x = 3227, y = 3214 } } } }] }, # lumbridge_south_tower_to_ground_floor - { from = { x = 3227, y = 3214 }, to = { x = 3229, y = 3214, level = 1 }, cost = 1, actions = [{ object = { option = "Climb-up", id = "36768", x = 3229, y = 3213, success = { tile = { level = 1 } } } }] }, # lumbridge_south_tower_ground_floor_to_1st_floor + { from = { x = 3227, y = 3214 }, to = { x = 3229, y = 3214, level = 1 }, cost = 1, actions = [{ object = { option = "Climb-up", id = "lumbridge_tower_ladder_south", x = 3229, y = 3213, success = { tile = { level = 1 } } } }] }, # lumbridge_south_tower_ground_floor_to_1st_floor { from = { x = 3229, y = 3214, level = 1 }, to = { x = 3229, y = 3214, level = 2 }, cost = 1, actions = [{ object = { option = "Climb-up", id = "36769", x = 3229, y = 3213, success = { tile = { level = 2 } } } }] }, # lumbridge_south_tower_1st_floor_to_2nd_floor { from = { x = 3229, y = 3214, level = 1 }, to = { x = 3229, y = 3214 }, cost = 1, actions = [{ object = { option = "Climb-down", id = "36769", x = 3229, y = 3213, success = { tile = { level = 0 } } } }] }, # lumbridge_south_tower_1st_floor_to_ground_floor { from = { x = 3229, y = 3214, level = 2 }, to = { x = 3229, y = 3214, level = 1 }, cost = 1, actions = [{ object = { option = "Climb-down", id = "36770", x = 3229, y = 3213, success = { tile = { level = 1 } } } }] }, # lumbridge_south_tower_2nd_floor_to_1st_floor diff --git a/data/entity/obj/stairs_ladders.objs.toml b/data/entity/obj/stairs_ladders.objs.toml index b3d9377f68..80070c3b00 100644 --- a/data/entity/obj/stairs_ladders.objs.toml +++ b/data/entity/obj/stairs_ladders.objs.toml @@ -114,7 +114,7 @@ examine = "I can climb this." id = 29355 examine = "I can climb this." -[36768] +[lumbridge_tower_ladder_south] id = 36768 examine = "I can climb up this." diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/interact/Interact.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/interact/Interact.kt index e2dc9308d3..eb3c2540a7 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/interact/Interact.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/interact/Interact.kt @@ -86,7 +86,6 @@ open class Interact( return } updateRange = false - val target = target if (stepOut()) { super.tick() return diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt index ef447a78c6..0e85c30275 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt @@ -59,17 +59,24 @@ open class Movement( */ protected open fun stepOut(): Boolean { val strategy = strategy ?: return false - if (strategy.shape != -2) return false + if (strategy.shape != -2) { + return false + } val npc = character as? NPC ?: return false - if (npc.def["allowed_under", false]) return false - if (!Overlap.isUnder(npc.tile, npc.size, npc.size, strategy.tile, strategy.width, strategy.height)) return false + if (npc.def["allowed_under", false]) { + return false + } + if (!Overlap.isUnder(npc.tile, npc.size, npc.size, strategy.tile, strategy.width, strategy.height)) { + return false + } clearSteps() - if (shouldQueueStepOut()) { - for (direction in Direction.cardinal.shuffled(random)) { - if (canStep(direction.delta.x, direction.delta.y)) { - character.steps.queueStep(npc.tile.add(direction)) - break - } + if (!shouldQueueStepOut()) { + return true + } + for (direction in Direction.cardinal.shuffled(random)) { + if (canStep(direction.delta.x, direction.delta.y)) { + character.steps.queueStep(npc.tile.add(direction)) + break } } return true @@ -85,7 +92,7 @@ open class Movement( if (character is Player && character.viewport?.loaded == false) { return } - if (hasDelay() && !canMove() && !character.steps.destination.noCollision) { + if (!canMove()) { return } if (!stepOut()) { @@ -101,17 +108,16 @@ open class Movement( } private fun canMove(): Boolean { - if (!hasDelay() && (character as? Player)?.menu == null) { - return true + if (character.hasClock("movement_delay")) { + return false } - if (character.queue.isEmpty()) { - return true + if (character.contains("delay")) { + // Inactive delays block movement unless there's a queue in action + return character.delay != null || !character.queue.isEmpty() || character.steps.destination.noCollision } - return character.delay != null + return true } - private fun hasDelay() = character.hasClock("movement_delay") || character.contains("delay") - /** * Applies one step * @return false if blocked by an obstacle or not [Character.steps] left to take diff --git a/game/src/main/kotlin/content/entity/effect/EffectCommands.kt b/game/src/main/kotlin/content/entity/effect/EffectCommands.kt index 5e261f5303..2824533aff 100644 --- a/game/src/main/kotlin/content/entity/effect/EffectCommands.kt +++ b/game/src/main/kotlin/content/entity/effect/EffectCommands.kt @@ -53,7 +53,7 @@ class EffectCommands( if (player.poisoned || ticks < 0) { target.softTimers.clear("movement_delay") } else { - target.freeze(ticks, force = true) + player.freeze(target, ticks, force = true) } } diff --git a/game/src/main/kotlin/content/entity/effect/Freeze.kt b/game/src/main/kotlin/content/entity/effect/Freeze.kt index 584f0f1c0a..30611d058c 100644 --- a/game/src/main/kotlin/content/entity/effect/Freeze.kt +++ b/game/src/main/kotlin/content/entity/effect/Freeze.kt @@ -38,23 +38,23 @@ fun Character.freeze(target: Character, ticks: Int, force: Boolean = false): Boo fun Character.freeze(ticks: Int, force: Boolean = false) { val protect = praying("protect_from_magic") || praying("deflect_magic") movementDelay = if (force || !protect) ticks else ticks / 2 - softTimers.start("movement_delay") + softTimers.start("frozen") } fun Character.freezeImmune(ticks: Int) { movementDelay = -ticks - softTimers.start("movement_delay") + softTimers.start("frozen") } class Freeze : Script { init { - timerStart("movement_delay", ::start) - npcTimerStart("movement_delay", ::start) - timerTick("movement_delay", ::tick) - npcTimerTick("movement_delay", ::tick) - timerStop("movement_delay", ::stop) - npcTimerStop("movement_delay", ::stop) + timerStart("frozen", ::start) + npcTimerStart("frozen", ::start) + timerTick("frozen", ::tick) + npcTimerTick("frozen", ::tick) + timerStop("frozen", ::stop) + npcTimerStop("frozen", ::stop) } fun start(character: Character, restart: Boolean): Int { diff --git a/game/src/main/kotlin/content/entity/player/combat/Attack.kt b/game/src/main/kotlin/content/entity/player/combat/Attack.kt index 30e122eb75..c06d7bada1 100644 --- a/game/src/main/kotlin/content/entity/player/combat/Attack.kt +++ b/game/src/main/kotlin/content/entity/player/combat/Attack.kt @@ -102,6 +102,17 @@ class Attack : Script { it.combatInteraction(target) } + onPlayerApproach("*_spellbook:*") { + val (target, id) = it + approachRange(8, update = false) + spell = id.substringAfter(":") + set("attack_speed", 5) + set("one_time", true) + attackRange = 8 + face(target) + it.combatInteraction(target) + } + combatPrepare { if (contains("one_time")) { mode = EmptyMode diff --git a/game/src/main/kotlin/content/skill/magic/book/SpellRunes.kt b/game/src/main/kotlin/content/skill/magic/book/SpellRunes.kt index ec4c37f765..4b74d88b1a 100644 --- a/game/src/main/kotlin/content/skill/magic/book/SpellRunes.kt +++ b/game/src/main/kotlin/content/skill/magic/book/SpellRunes.kt @@ -9,7 +9,7 @@ class SpellRunes : Script { init { combatPrepare(style = "magic") { _ -> if (spell.isNotBlank() && !hasSpellItems(spell)) { - clear("autocast") + clear("spell") false } else { true diff --git a/game/src/main/kotlin/content/skill/magic/spell/Spells.kt b/game/src/main/kotlin/content/skill/magic/spell/Spells.kt index 76b944e802..8babb90c70 100644 --- a/game/src/main/kotlin/content/skill/magic/spell/Spells.kt +++ b/game/src/main/kotlin/content/skill/magic/spell/Spells.kt @@ -21,7 +21,7 @@ class Spells : Script { return@combatAttack } if (spell.endsWith("_burst") || spell.endsWith("_barrage")) { - val targets = multiTargets(target, 9) + val targets = multiTargets(this, target, 9) for (targ in targets) { targ.directHit(this, random.nextInt(0..damage), type, weapon, spell) } diff --git a/game/src/main/kotlin/content/skill/melee/weapon/SpecialAttackMelee.kt b/game/src/main/kotlin/content/skill/melee/weapon/SpecialAttackMelee.kt index 9622b23c2b..f1a4f74243 100644 --- a/game/src/main/kotlin/content/skill/melee/weapon/SpecialAttackMelee.kt +++ b/game/src/main/kotlin/content/skill/melee/weapon/SpecialAttackMelee.kt @@ -8,13 +8,13 @@ import world.gregs.voidps.engine.entity.character.player.Players import world.gregs.voidps.engine.entity.character.player.skill.Skill import world.gregs.voidps.engine.map.spiral -fun multiTargets(target: Character, hits: Int): List { +fun multiTargets(source: Character, target: Character, hits: Int): List { val group = if (target is Player) Players else NPCs val targets = mutableListOf() for (tile in target.tile.spiral(1)) { val characters = group.at(tile) for (character in characters) { - if (character == target || !character.inMultiCombat) { + if (character == target || character == source || !character.inMultiCombat) { continue } targets.add(character) diff --git a/game/src/main/kotlin/content/skill/ranged/weapon/Chinchompa.kt b/game/src/main/kotlin/content/skill/ranged/weapon/Chinchompa.kt index 7e6fee8afb..a9a1a83634 100644 --- a/game/src/main/kotlin/content/skill/ranged/weapon/Chinchompa.kt +++ b/game/src/main/kotlin/content/skill/ranged/weapon/Chinchompa.kt @@ -19,7 +19,7 @@ class Chinchompa : Script { combatAttack("range") { (target, damage, type, weapon, spell) -> if (weapon.id.endsWith("chinchompa") && damage > 0 && target.inMultiCombat) { - val targets = multiTargets(target, if (target is Player) 9 else 11) + val targets = multiTargets(this, target, if (target is Player) 9 else 11) for (targ in targets) { targ.directHit(this, random.nextInt(0..damage), type, weapon, spell) } diff --git a/game/src/test/kotlin/content/entity/effect/FreezeTest.kt b/game/src/test/kotlin/content/entity/effect/FreezeTest.kt new file mode 100644 index 0000000000..5c1c5c7539 --- /dev/null +++ b/game/src/test/kotlin/content/entity/effect/FreezeTest.kt @@ -0,0 +1,28 @@ +package content.entity.effect + +import WorldTest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import world.gregs.voidps.engine.client.instruction.handle.interactObject +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.type.Tile + +class FreezeTest : WorldTest() { + + @Test + fun `Frozen players can't move or interact`() { + val player = createPlayer(Tile(3229, 3215)) + + player.freeze(10) + tick(1) + + player.walkTo(Tile(3228, 3215)) + tick(4) + assertEquals(Tile(3229, 3215), player.tile) + + val ladder = GameObjects.find(Tile(3229, 3213), "lumbridge_tower_ladder_south") + player.interactObject(ladder, "Climb-up") + tick(4) + assertEquals(Tile(3229, 3215), player.tile) + } +}