diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/handle/InterfaceOnPlayerOptionHandler.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/handle/InterfaceOnPlayerOptionHandler.kt index a48c61fc67..30b3bce033 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/handle/InterfaceOnPlayerOptionHandler.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/handle/InterfaceOnPlayerOptionHandler.kt @@ -1,11 +1,14 @@ package world.gregs.voidps.engine.client.instruction.handle +import com.github.michaelbull.logging.InlineLogger import world.gregs.voidps.engine.client.instruction.InstructionHandler import world.gregs.voidps.engine.client.instruction.InterfaceHandler import world.gregs.voidps.engine.client.ui.closeInterfaces import world.gregs.voidps.engine.entity.character.mode.interact.ItemOnPlayerInteract import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.Players +import world.gregs.voidps.engine.entity.character.player.name +import world.gregs.voidps.engine.entity.item.Item import world.gregs.voidps.network.client.instruction.InteractInterfacePlayer class InterfaceOnPlayerOptionHandler( @@ -16,9 +19,19 @@ class InterfaceOnPlayerOptionHandler( val (playerIndex, interfaceId, componentId, itemId, itemSlot) = instruction val target = Players.indexed(playerIndex) ?: return false - val (id, component, item) = handler.getInterfaceItem(player, interfaceId, componentId, itemId, itemSlot) ?: return false + if ((interfaceId == 192 || interfaceId == 193) && itemId == -1) { + player.closeInterfaces() + player["magic_spell"] = "$interfaceId:$componentId" + player["spellbook"] = if (interfaceId == 193) 1 else 0 + player.mode = ItemOnPlayerInteract(target, "$interfaceId:$componentId", Item.EMPTY, -1, player) + return true + } + + val (id, component, item) = handler.getInterfaceItem(player, interfaceId, componentId, itemId, itemSlot) + ?: return false player.closeInterfaces() player.mode = ItemOnPlayerInteract(target, "$id:$component", item, itemSlot, player) + return true } } 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..71e044ea49 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 @@ -130,7 +130,8 @@ open class Interact( interacted = false clearInteracted = false } - if (!character.hasMenuOpen()) { + // Don't move if we just interacted at range + if (!character.hasMenuOpen() && !interacted) { super.tick() } if (!interacted || updateRange) { diff --git a/game/src/main/kotlin/content/entity/combat/Combat.kt b/game/src/main/kotlin/content/entity/combat/Combat.kt index 92b9b25c44..660fc2338a 100644 --- a/game/src/main/kotlin/content/entity/combat/Combat.kt +++ b/game/src/main/kotlin/content/entity/combat/Combat.kt @@ -204,9 +204,12 @@ class Combat(val combatDefinitions: CombatDefinitions) : } else if (character is Player) { val style = character.fightStyle if (style == "magic" || style == "blaze") { - if (Magic.castSpell(character, target)) { - CombatApi.swing(character, target, character.weapon.id, style) + if (!Magic.castSpell(character, target)) { + character.mode = EmptyMode + character.target = null + return } + CombatApi.swing(character, target, character.weapon.id, style) } else { CombatApi.swing(character, target, character.weapon.id, style) } diff --git a/game/src/main/kotlin/content/skill/magic/CombatSpellsOnPlayer.kt b/game/src/main/kotlin/content/skill/magic/CombatSpellsOnPlayer.kt new file mode 100644 index 0000000000..bde7e84e86 --- /dev/null +++ b/game/src/main/kotlin/content/skill/magic/CombatSpellsOnPlayer.kt @@ -0,0 +1,49 @@ +package content.skill.magic + +import com.github.michaelbull.logging.InlineLogger +import content.skill.magic.spell.spell +import world.gregs.voidps.cache.definition.Params +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.variable.hasClock +import world.gregs.voidps.engine.client.variable.start +import world.gregs.voidps.engine.data.definition.InterfaceDefinitions +import world.gregs.voidps.engine.entity.Approachable +import world.gregs.voidps.engine.entity.character.mode.EmptyMode +import world.gregs.voidps.engine.entity.character.player.name + +class CombatSpellsOnPlayer : Script { + private val logger = InlineLogger() + + init { + Approachable.onPlayer.getOrPut("*") { mutableListOf() }.add { interact -> + val id = interact.id + if (!id.startsWith("192:") &&!id.startsWith("193:") &&!id.startsWith("194:") &&!id.startsWith("430:")) return@add + if (hasClock("action_delay")) return@add + + val parts = id.split(":") + val ifaceId = parts[0].toInt() + val compId = parts[1].toInt() + + val defs = InterfaceDefinitions.definitions + if (ifaceId >= defs.size) return@add + val def = defs[ifaceId] + val component = def.components?.get(compId)?: return@add + val spell = component.stringId + + if (component.params?.get(Params.id("cast_id")) == null) return@add + + logger.debug { "Spell $id ($spell) on ${interact.target.name}" } + approachRange(10) + this.spell = spell + set("one_time", true) + + start("action_delay", 4) + Magic.castSpell(this@add, interact.target) + + // End Interact, let Combat mode handle subsequent casts + if (mode == interact) { + mode = EmptyMode + } + } + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/content/skill/magic/Magic.kt b/game/src/main/kotlin/content/skill/magic/Magic.kt index afbca073be..8d2a27a209 100644 --- a/game/src/main/kotlin/content/skill/magic/Magic.kt +++ b/game/src/main/kotlin/content/skill/magic/Magic.kt @@ -19,7 +19,11 @@ import world.gregs.voidps.engine.get object Magic { fun castSpell(source: Character, target: Character): Boolean { if (source.spell.isNotBlank() && source is Player && !source.removeSpellItems(source.spell)) { - source.clear("autocast") + if (!source.contains("spell")) { + source.clear("autocast") + } + source.clear("spell") + source.clear("one_time") return false } val spell = source.spell 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..52b8cb2cb8 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,11 @@ class SpellRunes : Script { init { combatPrepare(style = "magic") { _ -> if (spell.isNotBlank() && !hasSpellItems(spell)) { - clear("autocast") + if (!contains("spell")) { + clear("autocast") + } + clear("spell") + clear("one_time") 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..e5a13f80d4 100644 --- a/game/src/main/kotlin/content/skill/magic/spell/Spells.kt +++ b/game/src/main/kotlin/content/skill/magic/spell/Spells.kt @@ -21,9 +21,11 @@ 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) + // damage can be -1 on a miss — clamp to 0 + val splash = if (damage > 0) random.nextInt(0..damage) else 0 + targ.directHit(this, splash, 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 aa7a7658cb..37d1219856 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") && 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) }