From efc324883bb150e450d811a9d9a466c931c9dd89 Mon Sep 17 00:00:00 2001 From: Tooze Date: Sat, 18 Jul 2026 13:06:23 +0000 Subject: [PATCH 1/4] Implemented berserker necklace effect --- .../node/entity/combat/MeleeSwingHandler.kt | 11 +++++++++-- .../node/entity/combat/equipment/ArmourSet.java | 17 ++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt index 91864ec53..7f9b668d8 100644 --- a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -253,10 +253,14 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) return 1.0 + (e!!.skills.maximumLifepoints - e.skills.lifepoints) * 0.01 } + var multiplier = 1.0 if(e is Player && e.isWearingVoid(CombatStyle.MELEE) && (skillId == Skills.ATTACK || skillId == Skills.STRENGTH)) { - return 1.1 + multiplier += 0.1 } - return 1.0 + if (e is Player && e.properties.armourSet === ArmourSet.BERSERKER_NECKLACE_SETS && skillId == Skills.STRENGTH) { + multiplier += 0.2 + } + return multiplier } override fun getArmourSet(e: Entity?): ArmourSet? { @@ -269,6 +273,9 @@ open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) if (ArmourSet.VERAC.isUsing(e)) { return ArmourSet.VERAC } + if (ArmourSet.BERSERKER_NECKLACE_SETS.isUsing(e)) { + return ArmourSet.BERSERKER_NECKLACE_SETS + } return if (ArmourSet.TORAG.isUsing(e)) { ArmourSet.TORAG } else super.getArmourSet(e) diff --git a/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java b/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java index f311b95e6..5b7a7e3af 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java @@ -8,6 +8,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import org.rs09.consts.Items; /** * Represents the types of armour sets. @@ -129,6 +130,15 @@ public enum ArmourSet { } return super.isUsing(e); } + }, + /** + * Berserker Necklace sets + */ + BERSERKER_NECKLACE_SETS(null, new int[][] { { Items.BERSERKER_NECKLACE_11128 }, { Items.TOKTZ_XIL_AK_6523, Items.TOKTZ_XIL_EK_6525, Items.TZHAAR_KET_EM_6527, Items.TZHAAR_KET_OM_6528 } }) { + @Override + public boolean isUsing(Entity e) { + return super.isUsing(e); + } }; /** @@ -148,8 +158,9 @@ public enum ArmourSet { */ private ArmourSet(Graphics endGraphic, int[][] set) { this.endGraphic = endGraphic; - this.sets = new Item[4][5]; + this.sets = new Item[set.length][]; for (int i = 0; i < set.length; i++) { + sets[i] = new Item[set[i].length]; for (int k = 0; k < sets[i].length; k++) { sets[i][k] = new Item(set[i][k]); } @@ -197,7 +208,7 @@ public enum ArmourSet { } } } - return hits == 4; + return hits == sets.length; } -} \ No newline at end of file +} From 37b30d7561f4643f34df1e87cbb9d43d79554a6a Mon Sep 17 00:00:00 2001 From: dam <27978131-real_damighty@users.noreply.gitlab.com> Date: Sat, 18 Jul 2026 16:10:00 +0300 Subject: [PATCH 2/4] Corrected implementation of dragon halberd special attack --- .../game/node/entity/combat/BattleState.java | 22 +++++++++++++++++-- .../node/entity/combat/CombatSwingHandler.kt | 8 ++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Server/src/main/core/game/node/entity/combat/BattleState.java b/Server/src/main/core/game/node/entity/combat/BattleState.java index e75aac19d..4a12b5966 100644 --- a/Server/src/main/core/game/node/entity/combat/BattleState.java +++ b/Server/src/main/core/game/node/entity/combat/BattleState.java @@ -9,6 +9,7 @@ import core.game.node.entity.combat.spell.CombatSpell; /** * Represents an entity's current battle state. + * * @author Emperor */ public final class BattleState { @@ -99,6 +100,7 @@ public final class BattleState { /** * Constructs a new {@code BattleState} {@Code Object} + * * @param victim The victim entity. */ public BattleState(Entity entity, Entity victim) { @@ -134,6 +136,7 @@ public final class BattleState { /** * Gets the estimatedHit. + * * @return The estimatedHit. */ public int getEstimatedHit() { @@ -142,6 +145,7 @@ public final class BattleState { /** * Sets the estimated hit. + * * @param estimatedHit The estimated hit to set. */ public void setEstimatedHit(int estimatedHit) { @@ -164,6 +168,7 @@ public final class BattleState { /** * Gets the recoilDamage. + * * @return The recoilDamage. */ public int getRecoilDamage() { @@ -172,6 +177,7 @@ public final class BattleState { /** * Sets the recoilDamage. + * * @param recoilDamage The recoilDamage to set. */ public void setRecoilDamage(int recoilDamage) { @@ -236,6 +242,7 @@ public final class BattleState { /** * Gets the maximumHit. + * * @return The maximumHit. */ public int getMaximumHit() { @@ -244,6 +251,7 @@ public final class BattleState { /** * Sets the maximumHit. + * * @param maximumHit The maximumHit to set. */ public void setMaximumHit(int maximumHit) { @@ -252,6 +260,7 @@ public final class BattleState { /** * Gets the rangeWeapon. + * * @return The rangeWeapon. */ public RangeWeapon getRangeWeapon() { @@ -260,6 +269,7 @@ public final class BattleState { /** * Sets the rangeWeapon. + * * @param rangeWeapon The rangeWeapon to set. */ public void setRangeWeapon(RangeWeapon rangeWeapon) { @@ -268,6 +278,7 @@ public final class BattleState { /** * Gets the ammunition. + * * @return The ammunition. */ public Ammunition getAmmunition() { @@ -276,6 +287,7 @@ public final class BattleState { /** * Sets the ammunition. + * * @param ammunition The ammunition to set. */ public void setAmmunition(Ammunition ammunition) { @@ -284,6 +296,7 @@ public final class BattleState { /** * Gets the frozen. + * * @return The frozen. */ public boolean isFrozen() { @@ -292,6 +305,7 @@ public final class BattleState { /** * Sets the frozen. + * * @param frozen The frozen to set. */ public void setFrozen(boolean frozen) { @@ -300,6 +314,7 @@ public final class BattleState { /** * Gets the style. + * * @return The style. */ public CombatStyle getStyle() { @@ -308,6 +323,7 @@ public final class BattleState { /** * Sets the style. + * * @param style The style to set. */ public void setStyle(CombatStyle style) { @@ -316,6 +332,7 @@ public final class BattleState { /** * Gets the armourEffect. + * * @return The armourEffect. */ public ArmourSet getArmourEffect() { @@ -324,6 +341,7 @@ public final class BattleState { /** * Sets the armourEffect. + * * @param armourEffect The armourEffect to set. */ public void setArmourEffect(ArmourSet armourEffect) { @@ -332,6 +350,7 @@ public final class BattleState { /** * Gets the attacking entity. + * * @return The entity. */ public Entity getAttacker() { @@ -340,10 +359,9 @@ public final class BattleState { public int getTotalDamage() { int hit = Math.max(estimatedHit, 0) + Math.max(secondaryHit, 0); - if (targets != null) { for (BattleState s : targets) { - if (s != null) { + if (s != null && s != this) { hit += Math.max(s.getEstimatedHit(), 0) + Math.max(s.getSecondaryHit(), 0); } } diff --git a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt index 2b151ba47..d4324b233 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt @@ -254,9 +254,15 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { ?: return InteractionType.STILL_INTERACT //if we cannot derive a direction, it's because both tiles are the same, so hand off control to the main logic which already handles this case var next = closestEntityTile - while (next.getDistance(closestVictimTile) > 3) { //skip the initial gap in distance if it exists, because standard pathfinding would already stop us before this point if something was between us and the NPC or vice versa + //Skip the initial gap in distance if it exists, because standard pathfinding would already stop us before this point if something was between us and the NPC or vice versa. + //A fixed-direction walk can only arrive within its starting distance in steps; on oblique approaches it + //passes beside the target and never converges, so the walk is hard-capped at that many steps. + val maxSkipSteps = next.getDistance(closestVictimTile).toInt() + 1 + for (i in 0 until maxSkipSteps) { + if (next.getDistance(closestVictimTile) <= 3) break next = next.transform(dir) } + if (next.getDistance(closestVictimTile) > 3) return InteractionType.STILL_INTERACT //never converged (oblique approach), so defer to the range checks instead var result: InteractionType = InteractionType.STILL_INTERACT val maxIterations = next.getDistance(closestVictimTile).toInt() From 592138d90448cbdad3919d950c5e7a97f9b1528f Mon Sep 17 00:00:00 2001 From: oftheshire Date: Sat, 18 Jul 2026 13:14:53 +0000 Subject: [PATCH 3/4] Rewrote ring of stone and easter ring, which also fixes a teleblock bug --- Server/data/configs/interface_configs.json | 6 + .../global/handlers/item/MorphItemListener.kt | 77 ++++++++++++ .../global/handlers/item/MorphItemPlugin.java | 113 ------------------ 3 files changed, 83 insertions(+), 113 deletions(-) create mode 100644 Server/src/main/content/global/handlers/item/MorphItemListener.kt delete mode 100644 Server/src/main/content/global/handlers/item/MorphItemPlugin.java diff --git a/Server/data/configs/interface_configs.json b/Server/data/configs/interface_configs.json index 95087003a..4377daff0 100644 --- a/Server/data/configs/interface_configs.json +++ b/Server/data/configs/interface_configs.json @@ -539,6 +539,12 @@ "walkable": "true", "tabIndex": "-1" }, + { + "id": "375", + "interfaceType": "3", + "walkable": "false", + "tabIndex": "-1" + }, { "id": "377", "interfaceType": "8", diff --git a/Server/src/main/content/global/handlers/item/MorphItemListener.kt b/Server/src/main/content/global/handlers/item/MorphItemListener.kt new file mode 100644 index 000000000..b0b2e3857 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/MorphItemListener.kt @@ -0,0 +1,77 @@ +package content.global.handlers.item + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +/** + * Handles morphing with the Ring of Stone and Easter Ring. + */ + +class MorphItemListener : InteractionListener, InterfaceListener { + + // the morphing items + private val morphItems = intArrayOf(Items.RING_OF_STONE_6583, Items.EASTER_RING_7927) + + // the NPCs to morph into + private val easterEggs = intArrayOf( + NPCs.EGG_3689, + NPCs.EGG_3690, + NPCs.EGG_3691, + NPCs.EGG_3692, + NPCs.EGG_3693, + NPCs.EGG_3694 + ) + private val stone = intArrayOf(NPCs.ROCKS_2626) + + // the "unmorph" interface + private val morphIface = 375 + + // equipping the morph items + override fun defineListeners() { + onEquip(morphItems) { player, item -> + morph(player, item.id) + return@onEquip false + } + } + + // the interface that appears when you morph + override fun defineInterfaceListeners() { + + // hitting the 'unmorph' button + on(morphIface){ player, _, _, buttonID, _, _ -> + when(buttonID) { + 3 -> closeAllInterfaces(player) + } + return@on true + } + + // after the interface closes + onClose(morphIface) { player, _ -> + unmorph(player) + return@onClose true + } + } + + // morphs the player + private fun morph(player: Player, item: Int) { + val morphId = when (item) { + Items.RING_OF_STONE_6583 -> stone.random() + else -> easterEggs.random() + } + playAudio(player, Sounds.EASTER06_HUMAN_INTO_EGG_1520) + stopWalk(player) + closeAllInterfaces(player) + player.appearance.transformNPC(morphId) + openInterface(player, morphIface) + } + + // unmorphs the player + private fun unmorph(player: Player) { + player.appearance.transformNPC(-1) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/MorphItemPlugin.java b/Server/src/main/content/global/handlers/item/MorphItemPlugin.java deleted file mode 100644 index 672de389f..000000000 --- a/Server/src/main/content/global/handlers/item/MorphItemPlugin.java +++ /dev/null @@ -1,113 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; -import core.tools.RandomFunction; -import org.rs09.consts.Sounds; - -import static core.api.ContentAPIKt.playAudio; - -/** - * Handles a morph item. - * @author Vexia - */ -@Initializable -public class MorphItemPlugin implements Plugin { - - /** - * The easter egg ids. - */ - protected static final int[] EASTER_EGG_IDS = new int[] { 3689, 3690, 3691, 3692, 3693, 3694 }; - - /** - * The morph component. - */ - private static final Component COMPONENT = new Component(375).setCloseEvent(new CloseEvent() { - - @Override - public boolean close(Player player, Component c) { - unmorph(player); - return true; - } - - }); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(7927).getHandlers().put("equipment", this); - ItemDefinition.forId(6583).getHandlers().put("equipment", this); - ClassScanner.definePlugin(new MorphInterfacePlugin()); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - final Player player = (Player) args[0]; - final Item item = (Item) args[1]; - switch (identifier) { - case "equip": - morph(player, item); - return false; - } - return true; - } - - /** - * Morphs the player. - * @param player the player. - * @param item the item. - */ - private void morph(Player player, Item item) { - int morphId = item.getId() == 6583 ? 2626 : EASTER_EGG_IDS[RandomFunction.random(EASTER_EGG_IDS.length)]; - playAudio(player, 1520); - player.getInterfaceManager().close(); - player.getAppearance().transformNPC(morphId); - player.getInterfaceManager().removeTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); - player.getLocks().lockMovement(GameWorld.getTicks() + 900000000); - player.getLocks().lockInteractions(GameWorld.getTicks() + 90000000); - player.getLocks().lockTeleport(GameWorld.getTicks() + 900000000); - player.getInterfaceManager().openSingleTab(COMPONENT); - player.getAppearance().sync(); - player.getWalkingQueue().reset(); - } - - /** - * Unmorphs the player. - * @param player the player. - */ - private static void unmorph(Player player) { - player.getAppearance().transformNPC(-1); - player.unlock(); - player.getInterfaceManager().restoreTabs(); - } - - /** - * Handles the morph interface plugin. - * @author Vexia - */ - public class MorphInterfacePlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(375).setPlugin(this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - player.getInterfaceManager().closeSingleTab(); - return true; - } - - } - -} From 5a37f2f8daab54676508b92fe60d4e4a0c6b3303 Mon Sep 17 00:00:00 2001 From: Tooze Date: Sat, 18 Jul 2026 13:15:40 +0000 Subject: [PATCH 4/4] Fixed mithril grappling tip smithing UI binding --- Server/src/main/content/global/skill/smithing/SmithingType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/src/main/content/global/skill/smithing/SmithingType.java b/Server/src/main/content/global/skill/smithing/SmithingType.java index fd38a6209..94465077e 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingType.java +++ b/Server/src/main/content/global/skill/smithing/SmithingType.java @@ -106,7 +106,7 @@ public enum SmithingType { /** * Grapple Tips */ - TYPE_GRAPPLE_TIP(1, 170, 171, new int[] { 175, 176, 175, 174, 173 }, 1), + TYPE_GRAPPLE_TIP(1, 170, 171, new int[] { 176, 175, 174, 173 }, 1), /** * The studs type.