Skip to content

feat: 15.25 Base Spell Power#797

Open
jprzimba wants to merge 5 commits into
game1525from
feat-base-spells
Open

feat: 15.25 Base Spell Power#797
jprzimba wants to merge 5 commits into
game1525from
feat-base-spells

Conversation

@jprzimba

@jprzimba jprzimba commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

15.25 — Spell Base Power & combat formula update

Summary

This PR aligns spell damage/healing with Tibia 15.25 mechanics, introducing per-spell Base Power as a first-class attribute and updating level contribution to the post-patch B(L) formula (13.05.12657). It also includes related protocol fixes from the same release cycle.


Spell Base Power

C++

  • Added basePower to Spell (getBasePower() / setBasePower()).
  • New Lua API: spell:basePower(value) / spell:basePower() (getter).
  • Combat callbacks now pass basePower automatically:
    • CALLBACK_PARAM_LEVELMAGICVALUE(player, level, maglevel, basePower)
    • CALLBACK_PARAM_SKILLVALUE(player, skill, attack, factor, basePower)
  • Base power is resolved from the casting spell (instantSpellName / runeSpellName).

Level contribution — B(L)

  • New helper getBaseDamageHealing(level) in tools.cpp / tools.hpp:

    S = floor((sqrt(2L + 2025) + 5) / 10)
    B(L) = floor((L + 1000) / S) + 50*S - 450
    
  • Combat::getLevelFormula now uses B(L) * 2 + ml * 3 instead of level * 2 + ml * 3.

  • Replaces legacy level / 5 and level * 0.2 in Lua spell scripts.

Lua helpers (register_spells.lua)

Function Purpose
calculateBaseDamageHealing(level) B(L) level bonus
calculateAttackValue(player, skill, weaponDamage) Monk/melee attack value (uses B(L))
calculateMonkSpellDamage(...) Monk formula: (basePower * attackValue) / 100 + (spellFactor * attackValue)
calculateKnightHealing(...) Knight heals with base power + shielding + ML scaling
calculateLevelMagicDamage(...) Generic magic strike/wave formulas

Spell scripts updated

  • 91 spells received spell:basePower(N) from Tibia Wiki data.
  • Monk attacks use calculateMonkSpellDamage with callback basePower.
  • Knight heals (Bruise Bane, Wound Cleansing, Fair/Intense Wound Cleansing) use calculateKnightHealing.
  • Shield Slam / Shield Bash use basePower from callback instead of hardcoded constants.
  • Magic / melee scripts migrated from level / 5calculateBaseDamageHealing(level).

Example:

spell:basePower(45)

function onGetFormulaValues(player, level, maglevel, basePower)
    local min = calculateBaseDamageHealing(level) + (maglevel * 1.403) + 8
    local max = calculateBaseDamageHealing(level) + (maglevel * 2.203) + 13
    return -min, -max
end

Banners

New banner types registered and wired:

Enum Trigger
BANNER_TYPE_WEEKLY_TASK_ANY_CREATURE Weekly task completed (ioweeklytasks.cpp)
BANNER_TYPE_PROMOTION_GRANTED NPC promotion (modules.lua) and !promotion
  • God talkaction /banner validates against a whitelist of BANNER_TYPE_* constants.

Files changed (high level)

Area Key files
Base Power C++ spells.hpp, spells.cpp, spell_functions.cpp, combat.cpp, tools.cpp
Spell formulas data/scripts/lib/register_spells.lua, data/scripts/spells/**

Test plan

  • Recompile the server (C++ changes required).
  • Cast spells with known base power (e.g. Energy Strike 45, Annihilation 125, Swift Jab 12) and compare damage vs Cyclopedia expectations at multiple levels.
  • Verify knight heals scale with B(L) + base power (Wound Cleansing, Bruise Bane).
  • Test monk spells (Flurry of Blows, Chained Penance, Spiritual Outburst) at low/high level.
  • Test Shield Slam / Shield Bash with shield equipped — damage uses basePower from spell.

Breaking changes

  • Lua onGetFormulaValues callbacks receive an extra basePower argument (backward compatible — unused params are ignored in Lua).
  • Level contribution in formulas changed from level/5 to B(L) — damage/healing curves differ at high levels (intended, matches Tibia 13.05.12657+).

Notes

@Dehdao

Dehdao commented Jun 24, 2026

Copy link
Copy Markdown

I did a small study on spell formulas, based on tooling to estimate damage and datapoints collected from global tibia.

These are by no means final, but should provide a decent starting/base point to progress from.

— Spell Damage Formulas (Finalized 2026-06-23)


Level Contribution: B(L)

All spells — damage, healing, every vocation — include B(L) as a base component.

S    = floor((sqrt(2×L + 2025) + 5) / 10)
B(L) = floor((L + 1000) / S) + 50×S - 450

Quick reference:

Level B(L)
1 0
100 20
200 40
300 60
500 100
800 150
1000 183

Skill Spells (Knight / Monk)

Formula:

avg = round(B(L) + (bp / 1000) × skill × atk + bp / 6)
Symbol Meaning
bp Base power (per-spell, see table below)
skill Weapon skill (sword/axe/club/fist depending on vocation)
atk Weapon attack value (from equipped weapon/handwrap)
1000 Skill factor (SF) — controls skill×atk scaling
6 Flat offset divisor (K)

Projections (typical gear):

Spell bp S100/A45/L300 S120/A50/L500 S140/A50/L1000
Brutal Strike 39 242 341 463
Whirlwind Throw 32 209 297 412
Berserk 44 265 371 498
Front Sweep 80 433 593 756
Fierce Berserk 92 489 667 842
Groundshaker 32 209 297 412
Annihilation 125 643 871 1079
Flurry of Blows 55 317 439 577
Sweeping Takedown 48 284 396 527
Chained Penance 70 387 532 685
Greater FoB 86 461 630 799

Magic Spells (Sorcerer / Druid / Paladin)

Formula:

avg = round(B(L) + (bp / 25) × mlvl + bp / 6)
Symbol Meaning
bp Base power (per-spell, see table below)
mlvl Magic level
25 Skill factor (SF) — real Tibia value, universal
6 Flat offset divisor (same as skill formula)

Projections (typical magic levels):

Spell bp ML60/L300 ML100/L500 ML150/L1000
— Sorcerer —
Death Strike 45 176 288 461
Fire Wave 40 163 267 430
Energy Beam 60 214 350 553
Lightning 110 342 558 861
Strong Energy/Flame Strike 125 381 621 954
Great Fire Wave 100 317 517 800
Energy Wave 150 445 725 1108
Great Energy/Death Beam 170 496 808 1231
Rage of the Skies 200 573 933 1416
Ultimate Energy/Flame Strike 210 599 975 1478
Hell's Core 250 702 1142 1725
— Druid —
Ice/Terra/Flame Strike 45 176 288 461
Ice Wave 35 150 246 399
Ice/Terra Burst & Strong Strikes 115 355 579 892
Terra Wave 120 368 600 923
Strong Ice Wave / Wrath of Nature 150 445 725 1108
Ultimate Ice/Terra Strike 195 561 913 1386
Eternal Winter 200 573 933 1416
— Paladin —
Divine Missile 60 214 350 553
Divine Barrage 140 419 683 1046
Divine Caldera 160 471 767 1170
Divine Grenade 190 548 892 1355
— Runes —
AoE Runes (GFB/Avalanche/etc.) 50 188 308 491
Explosion / Fireball / Icicle 60 214 350 553
Sudden Death Rune 150 445 725 1108

Shielding Spells (Shield Bash / Shield Slam)

Formula:

avg = round(B(L) + (bp / 1000) × shielding × shieldDef + bp / 6)

Same structure as melee — shielding × shieldDef replaces skill × atk.

Spell bp
Shield Bash 55
Shield Slam 52

Auto-Attack (All Vocations)

Formula:

o   = floor(6 × atk / 5) × (skill + 4) / 28

min = floor(B(L) + o × r / 2)
avg = floor(B(L) + o × r)
max = floor(B(L) + o × r × 2)
Symbol Meaning
atk Weapon attack value
skill Weapon skill
r 2 for Monk, 1 for all other vocations

No bp term. Auto-attack is computed separately from the spell system.


Base Power Reference

Attack Spells — Magic Level (Sorcerer / Druid / Paladin)

Spell Words Vocation bp
Apprentice's Strike exori min flam Sorcerer 15
Buzz exori infir vis Sorcerer 15
Chill Out exevo infir frigo hur Druid 10
Death Strike exori mort Sorcerer 45
Divine Barrage exori mas san Paladin 140
Divine Caldera exevo mas san Paladin 160
Divine Grenade exevo tempo mas san Paladin 190
Divine Missile exori san Paladin 60
Energy Beam exevo vis lux Sorcerer 60
Energy Strike exori vis Sorcerer, Druid 45
Energy Wave exevo vis hur Sorcerer 150
Eternal Winter exevo gran mas frigo Druid 200
Fire Wave exevo flam hur Sorcerer 40
Flame Strike exori flam Sorcerer 45
Forked Glacier Druid 97
Forked Thorns Druid 105
Great Death Beam exevo max mort Sorcerer 170
Great Energy Beam exevo gran vis lux Sorcerer 170
Great Fire Wave exevo gran flam hur Sorcerer 100
Hell's Core exevo gran mas flam Sorcerer 250
Ice Burst exevo ulus frigo Druid 115
Ice Strike exori frigo Druid 45
Ice Wave exevo frigo hur Druid 35
Lightning exori amp vis Sorcerer 110
Mud Attack exori infir tera Druid 15
Physical Strike exori moe ico Druid 50
Rage of the Skies exevo gran mas vis Sorcerer 200
Scorch exevo infir flam hur Sorcerer 10
Strong Energy Strike exori gran vis Sorcerer 125
Strong Flame Strike exori gran flam Sorcerer 125
Strong Ice Strike exori gran frigo Druid 115
Strong Ice Wave exevo gran frigo hur Druid 150
Strong Terra Strike exori gran tera Druid 115
Terra Burst exevo ulus tera Druid 115
Terra Strike exori tera Druid 45
Terra Wave exevo tera hur Druid 120
Ultimate Energy Strike exori max vis Sorcerer 210
Ultimate Flame Strike exori max flam Sorcerer 210
Ultimate Ice Strike exori max frigo Druid 195
Ultimate Terra Strike exori max tera Druid 195
Wrath of Nature exevo gran mas tera Druid 150

Attack Spells — Skill (Knight / Monk / Paladin)

Spell Words Vocation bp
Annihilation exori gran ico Knight 125
Berserk exori Knight 44
Brutal Strike exori ico Knight 39
Chained Penance exori med pug Monk 70
Devastating Knockout exori gran nia Monk 62
Double Jab exori pug Monk 40
Ethereal Barrage exori mas con Paladin 40
Ethereal Spear exori con Paladin 25
Executioner's Throw exori amp kor Knight 60
Fierce Berserk exori gran Knight 92
Flurry of Blows exori mas pug Monk 55
Forceful Uppercut exori gran pug Monk 130
Front Sweep exori min Knight 80
Greater Flurry of Blows exori gran mas pug Monk 86
Greater Tiger Clash exori nia Monk 44
Groundshaker exori mas Knight 32
Lesser Ethereal Spear exori infir con Paladin 9
Lesser Front Sweep exori infir min Knight 14
Mystic Repulse exori amp pug Monk 85
Spiritual Outburst exori gran mas nia Monk 42
Strong Ethereal Spear exori gran con Paladin 38
Sweeping Takedown exori mas nia Monk 48
Swift Jab exori infir pug Monk 12
Tiger Clash exori infir nia Monk 15
Whirlwind Throw exori hur Knight 32

Attack Spells — Shielding (Knight)

Spell Words bp
Shield Bash exori icu 55
Shield Slam exori gran icu 52

Runes — Magic Level (all vocations)

Rune Req. Lvl Req. mlvl bp
Avalanche Rune 30 4 50
Explosion Rune 31 6 60
Fireball Rune 27 4 60
Great Fireball Rune 30 4 50
Heavy Magic Missile Rune 25 3 30
Holy Missile Rune 27 4 70
Icicle Rune 28 4 60
Light Magic Missile Rune 15 0 15
Light Stone Shower Rune 1 0 10
Lightest Magic Missile Rune 1 0 10
Lightest Missile Rune 1 0 5
Stalagmite Rune 24 3 50
Stone Shower Rune 28 4 50
Sudden Death Rune 45 15 150
Thunderstorm Rune 28 4 50
Ultimate Healing Rune 24 4 250

Healing Spells — Magic Level

Spell Words Vocation bp
Bruise Bane exura infir ico Knight 15
Divine Healing exura san Paladin 250
Heal Friend exura sio Druid 260
Intense Healing exura gran All 120
Light Healing exura All 40
Magic Patch exura infir All 20
Mass Healing exura gran mas res Druid 200
Mass Spirit Mend exura mas nia Monk 800
Nature's Embrace exura gran sio Druid 2000
Restoration exura max vita Sorc, Druid 375
Restore Balance exura tio sio Monk 425
Salvation exura gran san Paladin 400
Spirit Mend exura gran tio Monk 220
Ultimate Healing exura vita Sorc, Druid 250

Healing Spells — Magic Level + Shielding (Knight)

Uses the same avg formula but with an extra shielding term (formula TBD):

Spell Words bp
Wound Cleansing exura ico 70
Fair Wound Cleansing exura med ico 225
Intense Wound Cleansing exura gran ico 500

Notes

  • Healing formula uses the same B(L) base but the full coefficient set is not yet confirmed — individual spell outputs may be tuned.
  • Wound Cleansing family additionally scales with shielding × shieldDef (same pattern as Shield Bash/Slam) — exact formula pending test data.
  • Auto-attack is computed separately from the spell system (no bp term).
  • Paladin distance spells (Ethereal Spear family) use different SF values from melee (SF ≈ 17–30) — treat separately when implementing.
  • DoT/condition spells (Curse, Electrify, Envenom, Ignite, etc.) are excluded — they use hardcoded tick values, not these formulas.

@jprzimba

Copy link
Copy Markdown
Collaborator Author

My entire PR was built based on the issue, and all basePower values ​​were taken from the Tibia Fandom wiki, it seems to work well in my tests—please test it yourself if possible and share your results.

@Dehdao

Dehdao commented Jun 26, 2026

Copy link
Copy Markdown

Spell Damage Formula Rework

Problem with the current approach

The current implementation handles spell damage in two inconsistent ways:

Skill spells (knight) compute min/max inline in each callback using hardcoded range coefficients applied to skill + atk:

-- current berserk
local min = B(level) + (skill + attack) * 0.5
local max = B(level) + (skill + attack) * 1.5
return -min * 1.1, -max * 1.1

Magic spells (sorc/druid/paladin) hardcode ml coefficients per spell, completely ignoring basePower:

-- current energy strike / flame strike
local min = B(level) + maglevel * 1.403 + 8
local max = B(level) + maglevel * 2.203 + 13

Issues with this approach:

  • Tuning a spell means touching the formula inside the callback, not a single basePower value.
  • Skill spells use skill + atk (sum), which grows slowly and symmetrically — a player with skill 120 and atk 60 gets the same weight as skill 60 and atk 120. Real Tibia uses the product skill × atk, which makes both stats matter independently.
  • Fierce Berserk's range is 1.1×–3× of (skill + 2*atk), producing swings like 378–880 at high levels — a 2.3× spread that makes the spell feel unreliable.
  • Every spell is its own snowflake. There is no single lever to adjust overall damage output.

Proposed approach

Two unified helper functions driven by a single basePower value per spell:

Skill-based spells (knight, monk):

avg = B(L) + (bp / 1000) × skill × atk + bp / 6

Magic-based spells (sorc, druid, paladin):

avg = B(L) + (bp / 25) × mlvl + bp / 6

Both return a symmetric ±10% range around avg:

min = floor(avg × 0.9)
max = ceil(avg × 1.1)

Where B(L) is the shared level bonus function (unchanged from the original codebase):

step = floor((√(2L + 2025) + 5) / 10)
B(L) = floor((L + 1000) / step) + 50 × step − 450

The coefficients 1/1000 (skill) and 1/25 (magic) were extracted from real Tibia data. They produce averages that closely match global Tibia values at typical stat ranges, with basePower as the per-spell tuning knob.

To adjust a spell up or down, you change one number:

spell:basePower(44)  -- increase to buff, decrease to nerf

A server-wide spellDamageMultiplier config applies on top of these base formulas for global output scaling.

What it looks like in practice

Berserk (skill-based) — before vs after:

-- CURRENT
function onGetFormulaValues(player, skill, attack, factor)
	local level = player:getLevel()
	local min = (calculateBaseDamageHealing(level)) + (skill + attack) * 0.5
	local max = (calculateBaseDamageHealing(level)) + (skill + attack) * 1.5
	return -min * 1.1, -max * 1.1
end
-- PROPOSED
function onGetFormulaValues(player, skill, attack, factor)
	local avg = spellSkillDamage(44, player:getLevel(), skill, attack)
	return -math.floor(avg * 0.9), -math.ceil(avg * 1.1)
end

Energy Strike (magic-based) — before vs after:

-- CURRENT
function onGetFormulaValues(player, level, maglevel, basePower)
	local min = (calculateBaseDamageHealing(level)) + (maglevel * 1.403) + 8
	local max = (calculateBaseDamageHealing(level)) + (maglevel * 2.203) + 13
	return -min, -max
end
-- PROPOSED
function onGetFormulaValues(player, level, maglevel)
	local avg = spellMagicDamage(45, level, maglevel)
	return -math.floor(avg * 0.9), -math.ceil(avg * 1.1)
end

The helper functions (spellSkillDamage, spellMagicDamage) live in a shared lib and implement the formulas above. Every spell callback becomes two lines, and basePower on the spell: registration is the only tuning surface.


Side-by-side comparison

Numbers below use spellDamageMultiplier = 1.0 (no global bonus applied) so the formula shapes can be compared directly.

Berserk (exori) — bp=44

Scenario Proposed Current
Lv100 sk50 atk40 103–127 71–170
Lv150 sk70 atk50 171–211 99–231
Lv200 sk80 atk52 207–254 116–261
Lv300 sk100 atk55 278–340 151–321
Lv400 sk120 atk60 363–445 187–385

Fierce Berserk (exori gran) — bp=92

Scenario Proposed Current
Lv100 sk50 atk40 197–241 179–451
Lv150 sk70 atk50 330–404 238–594
Lv200 sk80 atk52 394–482 266–651
Lv300 sk100 atk55 522–640 320–759
Lv400 sk120 atk60 682–834 378–880

Energy Strike / Flame Strike — Sorc/Druid — bp=45

Scenario Proposed Current
Lv100 ml15 49–61 49–66
Lv150 ml25 74–92 73–98
Lv200 ml40 108–132 104–141
Lv300 ml60 158–194 152–205
Lv400 ml80 208–256 200–269

Summary

Skill spells Magic spells
Average damage Proposed is higher at most stat ranges — skill×atk product scales faster than the current skill+atk sum Virtually identical at low-mid levels, converges at high levels
Spread Proposed ±10% vs current up-to-3× swings Proposed ±10% vs current ~1.57× spread
Tuning One basePower value per spell One basePower value per spell
Global scaling spellDamageMultiplier config spellDamageMultiplier config

@jprzimba

Copy link
Copy Markdown
Collaborator Author

Now you can test latest charges

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants