diff --git a/game/plugin/entity/combat/src/framework/attack/attack.kt b/game/plugin/entity/combat/src/framework/attack/attack.kt index a4fec851..0dceb956 100644 --- a/game/plugin/entity/combat/src/framework/attack/attack.kt +++ b/game/plugin/entity/combat/src/framework/attack/attack.kt @@ -1,12 +1,6 @@ -import AttackStyle.* -import AttackType.Ranged import org.apollo.game.model.Animation import org.apollo.game.model.entity.Mob import org.apollo.game.model.entity.Player -import org.apollo.game.model.entity.Skill -import org.apollo.game.plugins.api.attack -import org.apollo.game.plugins.api.defence -import org.apollo.game.plugins.api.skills abstract class Attack( @@ -37,39 +31,8 @@ abstract class Attack( } } - // @todo - refactor this out somewhere, all rolls are the same calculations - // (damage, hit + defence) - - val effectiveAttackBase = source.skills.attack.currentLevel - - //@todo - attack prayers - val effectiveAttackModifier = 1.0 - val attackStyleBonus = when (style) { - Accurate -> 3 - Controlled -> 1 - LongRanged -> 1 - else -> 0 - } - - val effectiveAttack = effectiveAttackBase * effectiveAttackModifier + attackStyleBonus + 8 - //@todo - get attack bonus from stats - val attackEquipmentBonus = 0 - val maxHitRoll = effectiveAttack * (attackEquipmentBonus + 64) - - val effectiveDefenceBase = target.skills.defence.currentLevel - - //@todo - defence prayers - val effectiveDefenceModifier = 1.0 - val defenceStyleBonus = when (style) { - Defensive, LongRanged -> 3 - Controlled -> 1 - else -> 0 - } - - val effectiveDefence = effectiveDefenceBase * effectiveDefenceModifier + defenceStyleBonus + 8 - //@todo - get defence bonus from stats - val defenceEquipmentBonus = 0 - val maxDefenceRoll = effectiveDefence * (defenceEquipmentBonus + 64) + val maxHitRoll = calculateBasicMaxRoll(RollType.Attack, source) + val maxDefenceRoll = calculateBasicMaxRoll(RollType.Defence, target) val accuracy = if (maxHitRoll > maxDefenceRoll) { 1 - (maxDefenceRoll + 2) / (2 * (maxHitRoll + 1)) @@ -113,29 +76,6 @@ abstract class BasicAttack( requirements: MutableList ) : Attack(speed, range, type, style, attackAnimation, requirements) { override fun maxDamage(source: Mob): Int { - - val effectiveStrengthBase = when (type) { - Ranged -> source.skillSet.getCurrentLevel(Skill.RANGED) - else -> source.skillSet.getCurrentLevel(Skill.STRENGTH) - } - - // @todo - prayer + others (?) - val effectiveStrengthModifier = 1.0 - - val hitStyleBonus = when (style) { - Aggressive -> 3 - LongRanged, Controlled -> 1 - Defensive -> 0 - Accurate -> if (type == Ranged) 3 else 0 - else -> 0 - } - - val effectiveStrength = Math.floor(effectiveStrengthBase * effectiveStrengthModifier) + hitStyleBonus + 8 - //@todo - get from combat bonuses - val strengthBonus = 0 - - val baseDamage = 0.5 + effectiveStrength * (strengthBonus + 64) / 640 - - return Math.floor(baseDamage).toInt() + return calculateBasicMaxRoll(RollType.Damage, source) } } diff --git a/game/plugin/entity/combat/src/framework/attack/attack_type.kt b/game/plugin/entity/combat/src/framework/attack/attack_type.kt index 4ca7fe78..c886999a 100644 --- a/game/plugin/entity/combat/src/framework/attack/attack_type.kt +++ b/game/plugin/entity/combat/src/framework/attack/attack_type.kt @@ -1,11 +1,11 @@ -enum class AttackStyle { - Accurate, - Aggressive, - Defensive, - Controlled, - AltAggressive, +enum class AttackStyle(val attackBonus: Int = 0, val defenceBonus: Int = 0, val strengthBonus: Int = 0) { + Accurate(attackBonus = 3, strengthBonus = 3), + Aggressive(strengthBonus = 3), + Defensive(defenceBonus = 3), + Controlled(attackBonus = 1, strengthBonus = 1, defenceBonus = 1), + AltAggressive(strengthBonus = 3), Rapid, - LongRanged + LongRanged(attackBonus = 1,strengthBonus = 1, defenceBonus = 3) } enum class AttackType { diff --git a/game/plugin/entity/combat/src/framework/attack/attack_utils.kt b/game/plugin/entity/combat/src/framework/attack/attack_utils.kt new file mode 100644 index 00000000..c3741125 --- /dev/null +++ b/game/plugin/entity/combat/src/framework/attack/attack_utils.kt @@ -0,0 +1,74 @@ +import AttackType.Magic +import AttackType.Ranged +import CombatBonus.MeleeStrength +import CombatBonus.RangedStrength +import org.apollo.game.model.entity.Mob +import org.apollo.game.plugins.api.* + +enum class RollType { + Attack, + Defence, + Damage +} + +fun calculateBasicMaxRoll(rollType: RollType, mob: Mob, modifiers: List = emptyList()): Int { + val style = mob.combatState.attack.style + val attackType = mob.combatState.attack.type + + if (attackType == Magic) { + throw IllegalStateException("Basic roll calculator called for a magic attack. Unsupported") + } + + val styleBonus = when (rollType) { + RollType.Attack -> style.attackBonus + RollType.Defence -> style.defenceBonus + RollType.Damage -> { + if (style == AttackStyle.Accurate && attackType != Ranged) { + 0 + } else { + style.strengthBonus + } + } + } + + val baseSkill = when (rollType) { + RollType.Attack -> { + if (attackType == Ranged) { + mob.skills.ranged + } else { + mob.skills.attack + } + } + RollType.Defence -> mob.skills.defence + RollType.Damage -> { + if (attackType == Ranged) { + mob.skills.ranged + } else { + mob.skills.strength + } + } + } + + val equipmentBonuses = mob.combatState.bonuses + val equipmentBonus = when (rollType) { + RollType.Attack -> equipmentBonuses.attack[attackType] + RollType.Defence -> equipmentBonuses.defence[attackType] + RollType.Damage -> { + if (attackType == Ranged) { + equipmentBonuses[RangedStrength] + } else { + equipmentBonuses[MeleeStrength] + } + } + } + + val modifier = modifiers.reduce { a, b -> a + b } + val effectiveLevel = baseSkill.currentLevel * modifier + styleBonus + 8 + val maxRoll = if (rollType == RollType.Damage) { + 0.5 + effectiveLevel * (equipmentBonus + 64) / 640 + } else { + effectiveLevel * (equipmentBonus + 64) + } + + return maxRoll.toInt() +} \ No newline at end of file diff --git a/game/plugin/entity/combat/src/framework/combat_bonuses.kt b/game/plugin/entity/combat/src/framework/combat_bonuses.kt index 7c180996..b5d81f10 100644 --- a/game/plugin/entity/combat/src/framework/combat_bonuses.kt +++ b/game/plugin/entity/combat/src/framework/combat_bonuses.kt @@ -1,46 +1,74 @@ -data class DamageBonuses(val stab: Int, val slash: Int, val crush: Int, val magic: Int, val range: Int) -data class CombatBonuses( - val attack: DamageBonuses, - val defence: DamageBonuses, - val meleeStrength: Int, - val rangedStrength: Int, - val prayer: Int -) - -class CombatBonusesBuilder { - var meleeStrength = 0 - var rangedStrength = 0 - var prayer = 0 - var attackBonuses = DamageBonuses(0, 0, 0, 0, 0) - var defenceBonuses = DamageBonuses(0, 0, 0, 0, 0) - - fun attack(configurer: DamageBonusesBuilder.() -> Unit) { - val builder = DamageBonusesBuilder() - builder.configurer() - - attackBonuses = builder.build() - } - - fun defence(configurer: DamageBonusesBuilder.() -> Unit) { - val builder = DamageBonusesBuilder() - builder.configurer() - - defenceBonuses = builder.build() - } - - fun build(): CombatBonuses { - return CombatBonuses(attackBonuses, defenceBonuses, meleeStrength, rangedStrength, prayer) - } +enum class CombatBonus { + MeleeStrength, + RangedStrength, + Prayer } -class DamageBonusesBuilder( - var stab: Int = 0, - var slash: Int = 0, - var crush: Int = 0, - var magic: Int = 0, - var range: Int = 0 +data class AttackBonuses(private val bonuses: Map) { + companion object { + fun default() = AttackBonusesBuilder().build() + } + + operator fun get(key: AttackType): Int = bonuses[key]!! +} + +data class CombatBonuses( + val attack: AttackBonuses, + val defence: AttackBonuses, + private val combatBonuses: Map ) { - fun build(): DamageBonuses { - return DamageBonuses(stab, slash, crush, magic, range) - } + companion object { + fun default() = CombatBonusesBuilder().build() + } + + operator fun get(key: CombatBonus): Int = combatBonuses[key]!! +} + +class CombatBonusesBuilder { + var meleeStrength = 0 + var rangedStrength = 0 + var prayer = 0 + var attackBonuses = AttackBonuses.default() + var defenceBonuses = AttackBonuses.default() + + fun attack(configurer: AttackBonusesBuilder.() -> Unit) { + val builder = AttackBonusesBuilder() + builder.configurer() + + attackBonuses = builder.build() + } + + fun defence(configurer: AttackBonusesBuilder.() -> Unit) { + val builder = AttackBonusesBuilder() + builder.configurer() + + defenceBonuses = builder.build() + } + + fun build(): CombatBonuses { + return CombatBonuses(attackBonuses, defenceBonuses, mapOf( + CombatBonus.MeleeStrength to meleeStrength, + CombatBonus.RangedStrength to rangedStrength, + CombatBonus.Prayer to prayer + )) + } +} + +class AttackBonusesBuilder( + var stab: Int = 0, + var slash: Int = 0, + var crush: Int = 0, + var magic: Int = 0, + var range: Int = 0 +) { + + fun build(): AttackBonuses { + return AttackBonuses(mapOf( + AttackType.Stab to stab, + AttackType.Slash to slash, + AttackType.Crush to crush, + AttackType.Magic to magic, + AttackType.Ranged to range + )) + } } \ No newline at end of file diff --git a/game/plugin/entity/combat/src/framework/combat_state.kt b/game/plugin/entity/combat/src/framework/combat_state.kt index 57ed5542..c5ce78ee 100644 --- a/game/plugin/entity/combat/src/framework/combat_state.kt +++ b/game/plugin/entity/combat/src/framework/combat_state.kt @@ -32,6 +32,7 @@ var Mob.combatAttackTick: Long by attribute("combat_attack_tick", 0) class CombatState(private val mob: Mob, var attack: Attack) { var target: Mob? by WeakRefHolder() + var bonuses = CombatBonuses.default() fun ticksSinceAttack(): Long { return mob.world.tick() - mob.combatAttackTick diff --git a/game/plugin/entity/combat/src/framework/equipment/weapon.kt b/game/plugin/entity/combat/src/framework/equipment/weapon.kt index a01f63b0..97eb8449 100644 --- a/game/plugin/entity/combat/src/framework/equipment/weapon.kt +++ b/game/plugin/entity/combat/src/framework/equipment/weapon.kt @@ -72,8 +72,8 @@ class WeaponBuilder(private val weaponClass: WeaponClass) { combatBonusesBuilder.prayer = value } - fun attackBonuses(configurer: DamageBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.attack(configurer) - fun defenceBonuses(configurer: DamageBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.defence(configurer) + fun attackBonuses(configurer: AttackBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.attack(configurer) + fun defenceBonuses(configurer: AttackBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.defence(configurer) fun build() = Weapon(weaponClass, combatBonusesBuilder.build(), specialAttack) } diff --git a/game/plugin/entity/combat/src/framework/equipment/weapon_class.kt b/game/plugin/entity/combat/src/framework/equipment/weapon_class.kt index d2f8cda2..06393d67 100644 --- a/game/plugin/entity/combat/src/framework/equipment/weapon_class.kt +++ b/game/plugin/entity/combat/src/framework/equipment/weapon_class.kt @@ -2,7 +2,7 @@ import org.apollo.game.model.Animation data class SpecialBar(val button: Int, val configId: Int) data class WeaponClassDetails(val widget: Int, val specialBar: SpecialBar?, val styles: List) -data class WeaponClassStyle(val button: Int, val configId: Int, val attackStyle: AttackStyle, val attack: Attack, val blockAnimation: Animation?) +data class WeaponClassStyle(val button: Int, val attackStyle: AttackStyle, val attack: Attack, val blockAnimation: Animation?) typealias WeaponClassConfigurer = WeaponClassDetailsBuilder.() -> Unit @@ -75,7 +75,6 @@ class WeaponClassStyleBuilder(val attackStyle: AttackStyle) { return WeaponClassStyle( button ?: throw IllegalStateException("Combat style button is required"), - 0, attackStyle, attack, blockAnimation