mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-07 08:39:17 +00:00
Clean-up damage/attack roll calculations and bonus builders
This commit is contained in:
@@ -1,12 +1,6 @@
|
|||||||
import AttackStyle.*
|
|
||||||
import AttackType.Ranged
|
|
||||||
import org.apollo.game.model.Animation
|
import org.apollo.game.model.Animation
|
||||||
import org.apollo.game.model.entity.Mob
|
import org.apollo.game.model.entity.Mob
|
||||||
import org.apollo.game.model.entity.Player
|
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(
|
abstract class Attack(
|
||||||
@@ -37,39 +31,8 @@ abstract class Attack(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo - refactor this out somewhere, all rolls are the same calculations
|
val maxHitRoll = calculateBasicMaxRoll(RollType.Attack, source)
|
||||||
// (damage, hit + defence)
|
val maxDefenceRoll = calculateBasicMaxRoll(RollType.Defence, target)
|
||||||
|
|
||||||
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 accuracy = if (maxHitRoll > maxDefenceRoll) {
|
val accuracy = if (maxHitRoll > maxDefenceRoll) {
|
||||||
1 - (maxDefenceRoll + 2) / (2 * (maxHitRoll + 1))
|
1 - (maxDefenceRoll + 2) / (2 * (maxHitRoll + 1))
|
||||||
@@ -113,29 +76,6 @@ abstract class BasicAttack(
|
|||||||
requirements: MutableList<AttackRequirement>
|
requirements: MutableList<AttackRequirement>
|
||||||
) : Attack(speed, range, type, style, attackAnimation, requirements) {
|
) : Attack(speed, range, type, style, attackAnimation, requirements) {
|
||||||
override fun maxDamage(source: Mob): Int {
|
override fun maxDamage(source: Mob): Int {
|
||||||
|
return calculateBasicMaxRoll(RollType.Damage, source)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
enum class AttackStyle {
|
enum class AttackStyle(val attackBonus: Int = 0, val defenceBonus: Int = 0, val strengthBonus: Int = 0) {
|
||||||
Accurate,
|
Accurate(attackBonus = 3, strengthBonus = 3),
|
||||||
Aggressive,
|
Aggressive(strengthBonus = 3),
|
||||||
Defensive,
|
Defensive(defenceBonus = 3),
|
||||||
Controlled,
|
Controlled(attackBonus = 1, strengthBonus = 1, defenceBonus = 1),
|
||||||
AltAggressive,
|
AltAggressive(strengthBonus = 3),
|
||||||
Rapid,
|
Rapid,
|
||||||
LongRanged
|
LongRanged(attackBonus = 1,strengthBonus = 1, defenceBonus = 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AttackType {
|
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)
|
enum class CombatBonus {
|
||||||
data class CombatBonuses(
|
MeleeStrength,
|
||||||
val attack: DamageBonuses,
|
RangedStrength,
|
||||||
val defence: DamageBonuses,
|
Prayer
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DamageBonusesBuilder(
|
data class AttackBonuses(private val bonuses: Map<AttackType, Int>) {
|
||||||
var stab: Int = 0,
|
companion object {
|
||||||
var slash: Int = 0,
|
fun default() = AttackBonusesBuilder().build()
|
||||||
var crush: Int = 0,
|
}
|
||||||
var magic: Int = 0,
|
|
||||||
var range: Int = 0
|
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 {
|
companion object {
|
||||||
return DamageBonuses(stab, slash, crush, magic, range)
|
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) {
|
class CombatState(private val mob: Mob, var attack: Attack) {
|
||||||
var target: Mob? by WeakRefHolder()
|
var target: Mob? by WeakRefHolder()
|
||||||
|
var bonuses = CombatBonuses.default()
|
||||||
|
|
||||||
fun ticksSinceAttack(): Long {
|
fun ticksSinceAttack(): Long {
|
||||||
return mob.world.tick() - mob.combatAttackTick
|
return mob.world.tick() - mob.combatAttackTick
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ class WeaponBuilder(private val weaponClass: WeaponClass) {
|
|||||||
combatBonusesBuilder.prayer = value
|
combatBonusesBuilder.prayer = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attackBonuses(configurer: DamageBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.attack(configurer)
|
fun attackBonuses(configurer: AttackBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.attack(configurer)
|
||||||
fun defenceBonuses(configurer: DamageBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.defence(configurer)
|
fun defenceBonuses(configurer: AttackBonusesBuilder.() -> Unit) = this.combatBonusesBuilder.defence(configurer)
|
||||||
|
|
||||||
fun build() = Weapon(weaponClass, combatBonusesBuilder.build(), specialAttack)
|
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 SpecialBar(val button: Int, val configId: Int)
|
||||||
data class WeaponClassDetails(val widget: Int, val specialBar: SpecialBar?, val styles: List<WeaponClassStyle>)
|
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
|
typealias WeaponClassConfigurer = WeaponClassDetailsBuilder.() -> Unit
|
||||||
|
|
||||||
@@ -75,7 +75,6 @@ class WeaponClassStyleBuilder(val attackStyle: AttackStyle) {
|
|||||||
|
|
||||||
return WeaponClassStyle(
|
return WeaponClassStyle(
|
||||||
button ?: throw IllegalStateException("Combat style button is required"),
|
button ?: throw IllegalStateException("Combat style button is required"),
|
||||||
0,
|
|
||||||
attackStyle,
|
attackStyle,
|
||||||
attack,
|
attack,
|
||||||
blockAnimation
|
blockAnimation
|
||||||
|
|||||||
Reference in New Issue
Block a user