Clean-up damage/attack roll calculations and bonus builders

This commit is contained in:
Gary Tierney
2018-01-01 05:50:54 +00:00
parent d392913356
commit 547ef907e1
7 changed files with 158 additions and 116 deletions
@@ -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<AttackRequirement>
) : 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)
}
}
@@ -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 {
@@ -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<Double> = 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()
}
@@ -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<AttackType, Int>) {
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<CombatBonus, Int>
) {
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
))
}
}
@@ -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
@@ -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)
}
@@ -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<WeaponClassStyle>)
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