All heroes share the same six base attributes but have different minimum and maximum limits based on their classes and different primary and secondary stats.
All classes will share the same attribute rarity system, where heroes whose total attributes add up to 0 ~ 299 will be common, 300 ~ 375 will be epic, and 376+ will be legendary.
Class
Primary Stat
Secondary Stat
Warrior
Strength
Vitality
Assassin
Agility
Strength
Mage
Intelligence
Will
Ranger
Strength
Agility
Berserker
Vitality
Endurance
Ninja
Agility
Vitality
Spiritualist
Intelligence
Endurance
Machinist
Intelligence
Agility
Strength
Strength affects the hero's physical damage dealt through attacks and skills in combat.
Agility
Agility affects the hero's turn order and critical rate % in combat.
Vitality
Vitality affects the hero's maximum HP in combat.
Endurance
Endurance affects the hero's physical defense in combat.
Intelligence
Intelligence affects the hero's magical damage dealt through attacks and skills in combat.
Will
Will affects the hero's magical defense in combat.
Combat Attributes
This section contains our formulas for calculating your heroes' combat attributes.
Level 1-10 has only 9 levels while every other tier has 10. To maintain the same combat stats as before, heroes under level 10 will have a 1-level penalty when applying the formula.
Health Points (HP)
// Get the raw max HP based on all sources of VIT.
const rawMaxHp = 5 * VIT * (1.5 + (LEVEL / 100) * 15);
// Get the raw equipment boosts ('HP %', 'HP')
const rawEquipmentHpBoost = ((rawMaxHp / 100) * HP% + HP);
// Get the total equipment boosts ('Equipment HP% Bonus')
const totalEquipmentHpBoost = (rawEquipmentHpBoost / 100) * (100 + EQP.HP%);
// Get the total HP (including combat boosts)
const baseMaxHp = Math.round((rawMaxHp + totalEquipmentHpBoost) * BOOSTS.HP%);
The hero's HP resets for every quest. You lose when your HP reaches 0.
Attack (P.ATK / M.ATK)
// Get the raw magic attack based on all sources of INT.
const baseMagicAttack = intelligence * (1.5 + (combatStatLevel / 100) * 15);
// Get the total M. ATK after applying raw and total equipment boosts.
const magicAttack = Math.round(
baseMagicAttack +
(((baseMagicAttack / 100) * M.ATK% +
M.ATK) /
100) *
(100 + EQP.ATK%)
);
// Same for physicalAttack. Replace M.ATK with P.ATK.
Basic attacks will deal damage based on your class's attribute.
STR is used for Physical Attack, INT is used for Magic Attack.
Physical Defense/Magic Resistance (DEF/RES)
// Get the raw defense based on all sources of END
const baseDefense = END * (1.5 + (LEVEL / 100) * 15);
// Get the total DEF after raw/total equipment and combat boosts.
// The flow is the same as in the HP formula, just substitute the values.
const defense = Math.round(
(baseDefense +
(((baseDefense / 100) * DEF% + DEF) / 100) *
(100 + EQP.DEF%)) *
BOOSTS.DEF%
);
DEF reduces the amount of physical damage received.
RES reduces the amount of magic damage received.
Other Formulas
The provided formulas below is pseudo-code for players to be able to follow the logic for analytics/calculations.
DISCLAIMER: Correct as of 29th November 2023, all subject to change.
// Crit Rate
const a = 6.26415119;
const b = 46.6345086;
const c = 0.58114342;
const d = 0.16064906;
const e = 86.56111256;
const critRate = a + b / (c + Math.exp(-d * (agi - e)));
Critical Damage/Resistance is applied after Final Attack!
See Base Damage Formula for where exactly it's applied.
// Return without deducting SP if dazed.
// Deduct SP cost of skill if necessary.
// If the skill can be shut down and the corresponding shutdown
// type matching the skill type is in effect, return.
// Start with our attack stat calculated from the hero + eqp + boosts.
const baseAttack = userPhysicalAttack || userMagicAttack;
// Roll for accuracy.
const accuracyRoll = RandomBetween(0, 100);
// Check for accuracy. The attack misses if:
// The user has no 'lock on' effect
// and skill is not guaranteed to hit
// and accuracyRoll > skillAccuracy%
// Check for crit. hasCritOnEffect is a status effect belonging to the user..
const critRoll = RandomBetween(0, 100);
const isCrit = hasCritOnEffect || critRoll < userCriticalRate;
// Track the actual damage dealt for recoil.
let actualDealtDamage = 0;
// For all targets... (including self if applicable, such as in status effect)
for (allTargets) {
// Check for requirements.
// etc. Check for requireBleed. Fail if target is not bleeding.
// etc. Check for requirePoison. Fail if target is not poisoned.
// etc. Check for requireDebuff. Fail if target is not debuffed.
// Calculated Defense = DEF/RES (based on skillProperty) * F.DEF%.
const calcDefense =
(targetDefense || targetResistance) / 100) *
(100 + targetFinalDefense%);
// Calculate total attack stat. getBaseAttack is used on skills with custom damage logic and overrides the attack stat
// etc. Berserk (based on HP%), Devour (*2), StatusEffects-only skills (0)
const calcAttack =
((isFunction(skill.getBaseAttack)
? skill.getBaseAttack()
: baseAttack) /
100) *
(100 + userFinalAttack%);
// Apply defense reduction to attack stat.
const initialDamage =
(calcAttack * RandomBetween(70, 130)) /
(calcDefense + 100);
// If this is a healing skill, apply heal instead and return.
if (isHealingSkill) {
// Calculate HP to recover/necro.
const extraHpToRecover = (initialDamage / 100) * targetLifeGain%;
const hpToRecover = initialDamage + extraHpToRecover;
// If target is necro-ed, this will deal damage instead of heal.
return;
}
const extraSkillDamage = isSkill
? (initialDamage / 100) * Math.max(0, userSkillDamage%)
: 0;
// Calculate extra revenge damage (30% damage increase if user below X% HP)
const userHpPerc = userHp / userMaxHp * 100;
const extraRevengeDamage =
userHpPerc < userRevengeThreshold% ? initialDamage * 0.3 : 0;
// Calculate skill damage reduction if skill.
const skillDefendedDamage = isSkill
? Math.max(
0,
((initialDamage + extraSkillDamage + extraRevengeDamage) / 100) * targetSkillDefense%
)
: 0;
// Finally, we get the hit damage. Return if this is <= 0.
const baseHitDamage =
initialDamage + extraSkillDamage + extraRevengeDamage - skillDefendedDamage;
// Check for dodge
const dodgeRoll = RandomBetween(0, 100);
// Check for target dodge. The attack is dodged if:
// The user has no 'lock on' effect
// and skill is not guaranteed to hit
// and dodgeRoll < targetDodgeRate%
// If target has protect status and skill can be protected against,
// Negate damage and return.
// Check for effects to apply on hit damage.
// Check for Lethal (+X% damage increase if target HP below 50%).
const targetHpPerc = target.hp / target.maxHp;
const extraLethalDamage =
targetHpPerc < 0.5 ? (baseHitDamage / 100) * userLethal% : 0;
// Calculate base damage after hit effects.
const baseDamage = Math.round(baseHitDamage + extraLethalDamage);
// Calculate critical damage bonus.
const rawCrit = (baseHitDamage / 100) * Math.max(0, userCriticalDamage%);
const rawCritRes = 100 - Math.min(100, targetCriticalResistance%);
const criticalDamage = isCrit ? (rawCrit / 100) * rawCritRes : 0;
// Get damage factoring armor.
const damageToReceive = baseDamage + criticalDamage - targetFlatDamageReduce;
// Apply modifiers to the damage itself, and generates status efects.
// etc. Aimed Shot, Convert X% HP into damage, attack for min. % HP
const { statusEffects, correctedDamage } =
getFunctionCodeResponse[skill.functionCode]({...});
if (correctedDamage > 0) {
const roundedDamage = Math.round(correctedDamage);
// Track the actual dealt damage.
actualDealtDamage += roundedDamage;
// Apply Damage Reflect
if (targetReflectHitRate% > 0) {
const reflectedDamage = Math.round((correctedDamage / 100) * targetReflectHitRate%);
}
// Apply Life Drain
if (userLifeDrainRate% > 0) {
const drainDamage = Math.round(
(correctedDamage / 100) * userLifeDrainRate%
);
}
// Apply Backlash Damage
if (userBacklashRate% > 0) {
const backlashDamage = Math.round(
(correctedDamage / 100) * userBacklashRate
);
}
}
}
// Handle Effects here.
// Handle Recoil.
if (skillHasRecoil) {
const recoilDamage = Math.round(actualDealtDamage * skillRecoil%);
if (recoilDamage > 0) {
// Apply Recoil Damage.
}
// END, continue to next user.
Attributes and Effects Glossary
Accuracy - % Chance for an attack/skill to succeed.
Critical Hit - Roll for critical hit based on Critical Rate % (unless the skill has special conditions). Deal additional damage based on Critical Damage %, reduced by the target's Critical Resistance %.
Require Bleed/Poison/Debuff/etc... - Attack/Skill will fail if target is not bleeding/poisoned/debuffed/etc...
Final Defense - A bonus applied on top of base Defense/Resistance depending on the incoming attack/skill type.
Final Attack - A bonus applied on top of the attack/skill base attack calculated
Necro - All sources of healing effects will deal damage instead
Skill Damage - Skills will deal an additional % damage. For players, skills are everything other than 'Attack'. For monsters, skills are everything except the monster's basic attack (which can have varying names and effects)
Revenge - Deal an extra fixed 30% damage if the user is below a % HP depending on the revenge threshold.
Skill Defense - Receive a % damage reduction against an incoming skill (revenge damage included). (Already applied DoTs do not count)
Dodge - % to avoid an attack/skill if the skill is not guaranteed to hit.
Protect - Negates all damage by an incoming attack/skill within the same turn.
Lethal - Deal extra % damage based on user's lethal threshold if target HP is below 50%.
Armor (flat damage reduce) - Reduces a FLAT amount of damage after all other effects are calculated.
Damage Reflect - User reflects % of true direct damage received to the attack/skill's user.
Life Drain - User drains % of true direct damage received. (affected by Necro)
Backlash - User receives a % of direct damage dealt as recoil.
Recoil Damage - User receives fixed damage based on a % of damage dealt.
The glossary is accurate as of v1.106.6 and may be changed further down as skills are implemented and balanced.