diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json index 6de270dfc..b2ee7cdf8 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -85597,7 +85597,9 @@ "destroy": "true", "weight": "4", "weapon_interface": "21", + "equip_audio": "732", "render_anim": "1277", + "attack_audios": "740,735,736,0", "name": "Orange salamander" }, { @@ -85624,60 +85626,66 @@ "destroy": "true", "weight": "4", "weapon_interface": "21", + "equip_audio": "732", "render_anim": "1277", + "attack_audios": "740,735,736,0", "name": "Red salamander" }, { - "requirements": "{0,70}-{4,70}-{6,70}", "ge_buy_limit": "1000", "turn90cw_anim": "5245", "examine": "Slightly slimy and somewhat menacing.", "walk_anim": "5245", - "durability": null, - "destroy": "true", - "weight": "4", "turn90ccw_anim": "5245", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "21", "turn180_anim": "5245", - "render_anim": "1277", "equipment_slot": "3", "grand_exchange_price": "124", "stand_anim": "5246", "tradeable": "true", - "name": "Black salamander", "run_anim": "824", "archery_ticket_price": "0", "id": "10148", "stand_turn_anim": "823", - "bonuses": "0,59,0,0,69,0,0,0,0,0,0,71,0,0,0" + "bonuses": "0,59,0,0,69,0,0,0,0,0,0,71,0,0,0", + "requirements": "{0,70}-{4,70}-{6,70}", + "durability": null, + "destroy": "true", + "weight": "4", + "weapon_interface": "21", + "equip_audio": "732", + "render_anim": "1277", + "attack_audios": "740,735,736,0", + "name": "Black salamander" }, { - "requirements": "{0,30}-{4,30}-{6,30}", "ge_buy_limit": "2000", "turn90cw_anim": "5245", "examine": "A very slimy and generally disgusting green lizard.", "walk_anim": "5245", - "durability": null, - "destroy": "true", - "weight": "4", "turn90ccw_anim": "5245", "attack_speed": "5", "two_handed": "true", - "weapon_interface": "21", "turn180_anim": "5245", - "render_anim": "1277", "equipment_slot": "3", "grand_exchange_price": "1802", "stand_anim": "5246", "tradeable": "true", - "name": "Swamp lizard", "run_anim": "824", "archery_ticket_price": "0", "id": "10149", "stand_turn_anim": "823", - "bonuses": "0,10,0,0,20,0,0,0,0,0,0,22,0,0,0" + "bonuses": "0,10,0,0,20,0,0,0,0,0,0,22,0,0,0", + "requirements": "{0,30}-{4,30}-{6,30}", + "durability": null, + "destroy": "true", + "weight": "4", + "weapon_interface": "21", + "equip_audio": "732", + "render_anim": "1277", + "attack_audios": "740,735,736,0", + "name": "Swamp lizard" }, { "shop_price": "4", diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java index 72e7e677d..a73af8457 100644 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ b/Server/src/main/java/core/game/node/entity/player/Player.java @@ -68,6 +68,7 @@ import rs09.ServerConstants; import rs09.game.VarpManager; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.node.entity.combat.equipment.EquipmentDegrader; +import rs09.game.node.entity.combat.handlers.SalamanderSwingHandler; import rs09.game.node.entity.player.graves.Grave; import rs09.game.node.entity.player.graves.GraveController; import rs09.game.node.entity.player.info.LogType; @@ -561,8 +562,8 @@ public class Player extends Entity { @Override public CombatSwingHandler getSwingHandler(boolean swing) { CombatStyle style = getProperties().getCombatPulse().getStyle(); + int weaponId = equipment.getNew(3).getId(); if (swing) { - int weaponId = equipment.getNew(3).getId(); if (getProperties().getSpell() != null || getProperties().getAutocastSpell() != null) { return CombatStyle.MAGIC.getSwingHandler(); } @@ -574,9 +575,12 @@ public class Player extends Entity { packetDispatch.sendMessage("Unhandled special attack for item " + weaponId + "!"); } } - if (style == CombatStyle.RANGE && equipment.getNew(3).getId() == 10033 || equipment.getNew(3).getId() == 10034) { + if (style == CombatStyle.RANGE && weaponId == 10033 || weaponId == 10034) { return ChinchompaSwingHandler.getInstance(); } + if (weaponId >= 10146 && weaponId <= 10149) { + return SalamanderSwingHandler.Companion.getINSTANCE(); + } return style.getSwingHandler(); } diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt index 3c7356293..2dc1a314d 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt @@ -20,6 +20,7 @@ import core.game.system.task.Pulse import rs09.game.world.GameWorld import core.game.world.update.flag.context.Animation import core.tools.RandomFunction +import rs09.game.node.entity.combat.handlers.SalamanderSwingHandler /** * The combat-handling pulse implementation. @@ -144,9 +145,10 @@ class CombatPulse( } var speed = entity.properties.attackSpeed val magic = handler!!.type == CombatStyle.MAGIC + val salamander = handler!! is SalamanderSwingHandler if (entity is Player && magic) { speed = 5 - } else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RAPID) { + } else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RAPID || (salamander && entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE)) { speed-- } if (!magic && entity.stateManager.hasState(EntityState.MIASMIC)) { diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt index 6c9a676f3..51f4537e2 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt @@ -1,27 +1,74 @@ package rs09.game.node.entity.combat.handlers +import api.EquipmentSlot +import api.getItemFromEquipment import core.game.node.entity.Entity import core.game.node.entity.combat.BattleState import core.game.node.entity.combat.CombatStyle import rs09.game.node.entity.combat.CombatSwingHandler import core.game.node.entity.combat.InteractionType +import core.game.node.entity.combat.equipment.Weapon +import core.game.node.entity.combat.equipment.WeaponInterface +import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills +import core.game.node.item.Item import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.Animations +import kotlin.math.ceil +import kotlin.math.floor /** * Handles a combat swing using a salamander. * @author Vexia + * @author itsmedoggo */ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandler(style) { - override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { - val index = entity!!.properties.attackStyle.style - style = when(index) { - 7 -> CombatStyle.MAGIC - 4 -> CombatStyle.RANGE - else -> CombatStyle.MELEE + override fun canSwing(entity: Entity, victim: Entity): InteractionType? { + checkStyle(entity) + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT } - return style.swingHandler.swing(entity, victim, state) + var distance = 1 + var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) + var type = InteractionType.STILL_INTERACT + if (victim.walkingQueue.isMoving && !goodRange) { + goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) + type = InteractionType.MOVE_INTERACT + } + if (goodRange && super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { + if (type == InteractionType.STILL_INTERACT) { + entity.walkingQueue.reset() + } + return type + } + return InteractionType.NO_INTERACT + } + + override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { + state!!.style = style + if (entity is Player) { + state.weapon = Weapon(entity.equipment[3]) + } + if (state!!.weapon == null || !SalamanderSwingHandler.hasAmmo(entity, state)) { + entity!!.properties.combatPulse.stop() + return -1 + } + var hit = 0 + if (isAccurateImpact(entity, victim, style)) { + val max = calculateHit(entity, victim, 1.0) + state.maximumHit = max + hit = RandomFunction.random(max + 1) + } + state.estimatedHit = hit + if (entity == null || victim!!.location == null) { + return -1 + } + if(state.estimatedHit > victim.skills.lifepoints) state.estimatedHit = victim.skills.lifepoints + SalamanderSwingHandler.useAmmo(entity, state) + return 1 } override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { @@ -29,7 +76,6 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle } override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { - style.swingHandler.visualize(entity, victim, state) entity.visualize(Animation.create(5247), Graphics(952, 100)) } @@ -38,10 +84,25 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle } override fun calculateAccuracy(entity: Entity?): Int { + //Checking style is necessary for ::calc_accuracy to function after changing attack style but before attacking + checkStyle(entity) return style.swingHandler.calculateAccuracy(entity) } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { + //Checking style is necessary for ::calcmaxhit to function after changing attack style but before attacking + checkStyle(entity) + if (style == CombatStyle.MAGIC) { + val level = entity!!.skills.getLevel(Skills.MAGIC, true) + var bonus = entity.properties.bonuses[13] + if (entity is Player) { + bonus += getSalamanderMagicDamageBonus(entity.equipment[3].id) + } + var cumulativeStr = level.toDouble() + cumulativeStr *= getSetMultiplier(entity, Skills.MAGIC) + cumulativeStr *= (bonus + 64) + return floor((0.5 + (ceil(cumulativeStr) / 640.0)) * modifier).toInt() + } return style.swingHandler.calculateHit(entity, victim, modifier) } @@ -66,8 +127,17 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle return style.swingHandler.getSetMultiplier(e, skillId) } - override fun canSwing(entity: Entity, victim: Entity): InteractionType? { - return style.swingHandler.canSwing(entity, victim) + /** + * Sets the local style + * @param e The entity + */ + private fun checkStyle(e: Entity?) { + val index = e!!.properties.attackStyle.style + style = when(index) { + WeaponInterface.STYLE_DEFENSIVE_CAST -> CombatStyle.MAGIC + WeaponInterface.STYLE_RANGE_ACCURATE -> CombatStyle.RANGE + else -> CombatStyle.MELEE + } } companion object { @@ -75,6 +145,91 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle * The instance for the swing handler. */ val INSTANCE = SalamanderSwingHandler(CombatStyle.MELEE) + + /** + * Gets the id of the tar used by a salamander. + * @param id The salamander id. + * @return The tar id. + */ + fun getAmmoId(id: Int): Int { + when (id) { + Items.SWAMP_LIZARD_10149 -> { + return Items.GUAM_TAR_10142 + } + Items.ORANGE_SALAMANDER_10146 -> { + return Items.MARRENTILL_TAR_10143 + } + Items.RED_SALAMANDER_10147 -> { + return Items.TARROMIN_TAR_10144 + } + Items.BLACK_SALAMANDER_10148 -> { + return Items.HARRALANDER_TAR_10145 + } + else -> { + return -1 + } + } + } + + /** + * Gets the magic damage bonus of a salamander. + * @param id The salamander id. + * @return The magic damage bonus. + */ + fun getSalamanderMagicDamageBonus(id: Int): Int { + when (id) { + Items.SWAMP_LIZARD_10149 -> { + return 56 + } + Items.ORANGE_SALAMANDER_10146 -> { + return 59 + } + Items.RED_SALAMANDER_10147 -> { + return 77 + } + Items.BLACK_SALAMANDER_10148 -> { + return 92 + } + else -> { + return 0 + } + } + } + + /** + * Checks if the entity has the ammunition needed to proceed. + * @param e The entity. + * @param state The battle state. + * @return `True` if so. + */ + fun hasAmmo(e: Entity?, state: BattleState?): Boolean { + if (e !is Player) { + return true + } + val ammo = getItemFromEquipment(e, EquipmentSlot.AMMO) + if (ammo == null) { + e.packetDispatch.sendMessage("You do not have enough ammo left.") + return false + } + if (ammo.id == (getAmmoId(state!!.weapon.id))) { + return true + } + e.packetDispatch.sendMessage("You can't use this type of ammunition with this salamander.") + return false + } + + /** + * Uses the ammunition for the range weapon. + * @param e The entity. + * @param state The battle state. + * @param location The drop location. + */ + fun useAmmo(e: Entity, state: BattleState) { + if (e !is Player) { + return + } + e.equipment.remove(Item(getAmmoId(state!!.weapon.id), 1)) + } } } \ No newline at end of file