# Damage Formula

<script>
import 'src/components/d3/d3-grid-contours.js';

function dataMatrix(fn) {
  let data = [];

  for(let x = 0; x <= 400; x += 20) {
    for(let y = 0; y <= 400; y += 20) {
      data.push([x, y, fn(x, y)]);
    }
  }
  return data
}

async function getChart() {
  let chart = await lively.create("d3-grid-contours")
  chart.style.width = "500px"
  chart.style.height = "500px"
  await chart.loaded
  chart.config({
    labelX: 'ATK',
    labelY: 'DEF',
  })
  return chart
}

const getDamageFormulaByClass = cls => {
  const source = lively.query(this, '.' + cls).innerText;
  return eval(source)
}

async function plot(cls) {
  const chart = await getChart();
  chart.setData(dataMatrix(getDamageFormulaByClass(cls)));
  return <div>{chart}</div>;
}
</script>

# Case Studies

## Final Fantasy X

Probably unrealistic, but very high *def* values **increase** the damage!
-> Applying the stat cap of 255 'solves' this issue

[](https://gamefaqs.gamespot.com/ps2/197344-final-fantasy-x/faqs/31381)

### Physical Damage

```javascript {.final-fantasy-x}
(atk, def) => {
  atk = Math.min(255, atk)
  def = Math.min(255, def)

  // Attack Power
  const cheerUser = 2; // how often was cheer applied to user in battle?
  const stat = atk + cheerUser;
  const damageConstant = 16; // attack command
  let baseDamage = ((stat ** 3 / 32) + 32) * damageConstant / 16;
  
  // Defense Subtraction
  const DefNum = Math.round((((def - 280.4) ** 2) / 110) + 16)
  baseDamage = baseDamage * DefNum / 730
  
  const cheerTarget = 0; // up to 5
  baseDamage = baseDamage * (15-cheerTarget)/15
  
  return baseDamage * (730 - (def * 51 - def ** 2 / 11) / 10) / 730
}
```

<script>
plot('final-fantasy-x')
</script>

## Persona 5 Royal

[](https://megamitensei.fandom.com/wiki/Damage)

```javascript {.persona-5-royal}
(atk, def) => {
  /// Attack Power
  const skillPower = 150 // Heat Wave, Heavy Physical damage
  // alternative 1/2 weaponPower for melee and gun attacks
  const basePower = Math.sqrt(skillPower) * Math.sqrt(atk)
  // When the user's Strength stat is doubled, the modifier will be multiplied by approximately 1.4
  
  // Defense Subtraction
  const armorDefense = 30
  const endurance = def
  const baseDamage = basePower / Math.sqrt(endurance * 8 + armorDefense);
  // If the def and endurance stats are doubled, the damage taken will be multiplied by approximately x0.7
  
  // damage manipulation through passives is capped to x2
  // level difference apply x0.5 - 1.5
  return baseDamage
}
```

<script>
plot('persona-5-royal')
</script>

## Bravely Second

[treasure trove of actual mechanics like group scaling!!](https://docs.google.com/document/d/1fORs07Ri92c4lUYQoLZN46hBSjs1qGnoNVI61nkCYzQ/edit#heading=h.c0lo0e8boeal)

#### Standard Attack Example

```javascript {.bravely}
(atk, def) => {
  const baseDamageMultiplier = 0.8
  const defenseMultiplier = 0.5
  return Math.max(0, baseDamageMultiplier * (atk - (def * defenseMultiplier)))
  
  
  const jobHitCountScaling = 15; // Ranger
  const AGI = 1.2; // Ranger
  const level = 20;
  const maxHitCount = 1 + Math.floor(AGI / 10) + Math.floor(level/ jobHitCountScaling)

  const hitCount = maxHitCount; // actually only for abilities
  const attackPower = 0; // standard attack
  const damageScaling = 1;
  
  const random = .9; // 0.8~1
  return (atk + attackPower - def) * hitCount * damageScaling * random
}
```

<script>
plot('bravely')
</script>

## Octopath Traveler

- [https://octopathtraveler.fandom.com/wiki/Damage_Formula]()
- [https://octopathtraveler.fandom.com/wiki/Level_Multiplier]()
- [https://www.reddit.com/r/octopathtraveler/comments/126xuv5/damage_calculation_guide/]()

#### Standard Attack Example

```javascript {.octopath}
(atk, def) => {
  const baseDamageMultiplier = 0.8
  const defenseMultiplier = 0.5
  return Math.max(0, baseDamageMultiplier * (atk - (def * defenseMultiplier)))
}
```

<script>
plot('octopath')
</script>

## Pokemon
[https://bulbapedia.bulbagarden.net/wiki/Damage]()

```javascript {.pokemon}
(atk, def) => {
  const level = 50
  const movePower = 80
  const random = 1; // [.85, 1]
  return ((2 * level / 5 + 2) * movePower * atk / Math.max(1, def)) * random
}
```

<script>
plot('pokemon')
</script>

## CrossCode
[https://www.reddit.com/r/CrossCode/comments/l1m3us/what_is_the_damage_formula_for_this_game/]()

[damage factor sheet](https://docs.google.com/spreadsheets/d/1J-fFeoubusXOKQNS8vbW2Jl-C1bpKzpcJJL_bpP9fAk/edit#gid=0)

```javascript {.cross-code}
const c = 100;
(atk, def) => {
  let stat_dmg_mod;
  if (atk > def) {
    stat_dmg_mod = 1 + ((1-def/atk) ** .5 ) * .2
  } else {
    stat_dmg_mod = (atk/def) ** 1.5 
  }
  const atk_dmg_factor = 1
  const other_mods = 1;
  const crit_mod = 1;
  return atk * atk_dmg_factor * stat_dmg_mod * other_mods * crit_mod 
}
```

<script>
plot('cross-code')
</script>

[https://steamcommunity.com/app/368340/discussions/0/3102389184732255900/]()

```javascript {.cross-code2}
const c = 100;
(atk, def) => {
var damage
const defensiveFactor = 1;
if(atk > def) {
  damage = Math.max(1, atk * (1 + Math.pow(1 - def / atk, 0.5) * 0.2)) * defensiveFactor;
} else {
  damage = Math.max(1, atk * Math.pow(atk / def, 1.5)) * defensiveFactor;
}

  const crit_mod = 1;
  return damage
}
```

<script>
plot('cross-code2')
</script>

---

##### w/o atk multiplication

```javascript {.cross-code3}
const c = 100;
(atk, def) => {
var damage
const defensiveFactor = 1;
if(atk > def) {
  damage = (1 + Math.pow(1 - def / atk, 0.5) * 0.2)
} else {
  damage = Math.pow(atk / def, 1.5)
}

  const crit_mod = 1;
  return damage
}
```

<script>
plot('cross-code3')
</script>

```javascript {.cross-code4}
(atk, def) => {
atk = Math.max(1, atk)
def = Math.max(1, def)
var damage
if(atk > def) {
  damage = 1 + Math.pow(1 - def / atk, 0.5) * 0.2
} else {
  damage = Math.pow(atk / def, 1.5)
}

  const crit_mod = 1;
  return damage
}
```

<script>
plot('cross-code4')
</script>

---

# General Approaches

## squaring-combined
[https://tung.github.io/posts/simplest-non-problematic-damage-formula/]()

```javascript {.squaring-combined}
(atk, def) => {
  if (atk >= def) {
    return atk * 2 - def;
  } else {
    return atk * atk / def;
  }
}
```

<script>
plot('squaring-combined')
</script>

## squaring??
[https://tung.github.io/posts/simplest-non-problematic-damage-formula/]()

```javascript {.squaring }
(atk, def) => {
  return atk * atk / Math.max(1, def);
}
```

<script>
plot('squaring ')
</script>

## Exponential??
[https://yujiri.xyz/game-design/damage-formulas.gmi]()

the effects of every stat scales exponentially, the formula is damage = c^(attack - defense) 
```javascript {.exponential}
const c = ([1.01, 1.1], 1.01);
(atk, def) => {
  return c ** (atk - def) 
}
```

<script>
plot('exponential')
</script>

## Additive
[https://yujiri.xyz/game-design/damage-formulas.gmi]()

```javascript {.additive}
const c = 100;
(atk, def) => {
  return c * atk / (c + def)
}
```

**Problem:** constant `c`. If difference of *ATK* and *DEF* is exactly `c` (which is a diagonal starting in ATK axis at value of `c`), we deal exactly `c` damage. Afterwards, damage trajectory falls onto ATK axis, before it falls onto the DEF axis; skewing in any direction.

<script>
plot('additive')
</script>

## linear percent??
[https://yujiri.xyz/game-design/damage-formulas.gmi]()

```javascript {.linear-percent}
(atk, def) => {
  return atk * (1 / Math.max(1, def))
}
```

<script>
plot('linear-percent')
</script>

## Double ATK - DEF

```javascript {.double-atk-def}
(atk, def) => {
  return Math.max(1, atk * 2 - def)
}
```

<script>
plot('double-atk-def')
</script>

## ATK - halved DEF

```javascript {.atk-halved-def}
(atk, def) => {
  return Math.max(1, atk - def / 2)
}
```

<script>
plot('atk-halved-def')
</script>

## ATK - DEF

```javascript {.atk-def}
(atk, def) => {
  return Math.max(1, atk - def)
}
```

<script>
plot('atk-def')
</script>

