Compare commits

...

50 Commits

Author SHA1 Message Date
troytt 210d59e3f2 feat(skills): implement 1.13c Cleansing [Skill #109] (fixes #263) 2026-09-22 03:45:15 +00:00
troytt ea430c1522 feat(skill-109): implement 1.13c Cleansing module and Vitest suite (#263) 2026-09-22 03:45:14 +00:00
troytt 246e1580a1 feat(skills): implement 1.13c Blessed Aim [Skill #108] (fixes #262) 2026-09-22 03:45:13 +00:00
troytt 60bd856fcf feat(skill-108): implement 1.13c Blessed Aim module and Vitest suite (#262) 2026-09-22 03:45:12 +00:00
troytt 7d4bd0717e feat(skills): implement 1.13c Charge [Skill #107] (fixes #261) 2026-09-22 03:45:11 +00:00
troytt 1609996501 feat(skill-107): implement 1.13c Charge module and Vitest suite (#261) 2026-09-22 03:45:11 +00:00
troytt c4bd201b27 feat(skills): implement 1.13c Zeal [Skill #106] (fixes #260) 2026-09-22 03:45:10 +00:00
troytt b4e564fc77 feat(skill-106): implement 1.13c Zeal module and Vitest suite (#260) 2026-09-22 03:45:09 +00:00
troytt 166cd4ab7e feat(skills): implement 1.13c Resist Cold [Skill #105] (fixes #259) 2026-09-22 03:45:08 +00:00
troytt 55f3616016 feat(skill-105): implement 1.13c Resist Cold module and Vitest suite (#259) 2026-09-22 03:45:07 +00:00
troytt c02b66b83a feat(skills): implement 1.13c Defiance [Skill #104] (fixes #258) 2026-09-22 03:45:06 +00:00
troytt eb7bd41f23 feat(skill-104): implement 1.13c Defiance module and Vitest suite (#258) 2026-09-22 03:45:06 +00:00
troytt e32c46b18b feat(skills): implement 1.13c Thorns [Skill #103] (fixes #257) 2026-09-22 03:45:04 +00:00
troytt c77fa985cb feat(skill-103): implement 1.13c Thorns module and Vitest suite (#257) 2026-09-22 03:45:04 +00:00
troytt fbc2118a26 feat(skills): implement 1.13c Holy Fire [Skill #102] (fixes #256) 2026-09-22 03:45:03 +00:00
troytt 7978d6bf08 feat(skill-102): implement 1.13c Holy Fire module and Vitest suite (#256) 2026-09-22 03:45:02 +00:00
troytt fbcf9c82c0 feat(skills): implement 1.13c Holy Bolt [Skill #101] (fixes #255) 2026-09-22 03:45:01 +00:00
troytt 06611910af feat(skill-101): implement 1.13c Holy Bolt module and Vitest suite (#255) 2026-09-22 03:45:00 +00:00
troytt 15e96acaa0 feat(skills): implement 1.13c Resist Fire [Skill #100] (fixes #254) 2026-09-22 03:44:59 +00:00
troytt c11219dd9d feat(skill-100): implement 1.13c Resist Fire module and Vitest suite (#254) 2026-09-22 03:44:58 +00:00
troytt 11ee925c2e feat(skills): implement 1.13c Prayer [Skill #099] (fixes #253) 2026-09-22 03:44:57 +00:00
troytt 104b4d6c62 feat(skill-099): implement 1.13c Prayer module and Vitest suite (#253) 2026-09-22 03:44:57 +00:00
troytt 02e7080aec feat(skills): implement 1.13c Might [Skill #098] (fixes #252) 2026-09-22 03:44:56 +00:00
troytt 50b5d95067 feat(skill-098): implement 1.13c Might module and Vitest suite (#252) 2026-09-22 03:44:55 +00:00
troytt 63b109b473 feat(skills): implement 1.13c Smite [Skill #097] (fixes #251) 2026-09-22 03:44:54 +00:00
troytt f0b9e7f834 feat(skill-097): implement 1.13c Smite module and Vitest suite (#251) 2026-09-22 03:44:53 +00:00
troytt 522ba0bcf2 feat(skills): implement 1.13c Sacrifice [Skill #096] (fixes #250) 2026-09-22 03:44:52 +00:00
troytt b1a72bbdf6 feat(skill-096): implement 1.13c Sacrifice module and Vitest suite (#250) 2026-09-22 03:44:52 +00:00
troytt c42275aab1 feat(skills): implement 1.13c Revive [Skill #095] (fixes #249) 2026-09-22 03:44:51 +00:00
troytt 84956115dd feat(skill-095): implement 1.13c Revive module and Vitest suite (#249) 2026-09-22 03:44:50 +00:00
troytt 3ee8d2057e feat(skills): implement 1.13c FireGolem [Skill #094] (fixes #248) 2026-09-22 03:44:49 +00:00
troytt 3fba1e79bc feat(skill-094): implement 1.13c FireGolem module and Vitest suite (#248) 2026-09-22 03:44:48 +00:00
troytt 6f8e6052ab feat(skills): implement 1.13c Bone Spirit [Skill #093] (fixes #247) 2026-09-22 03:44:47 +00:00
troytt 536d736803 feat(skill-093): implement 1.13c Bone Spirit module and Vitest suite (#247) 2026-09-22 03:44:47 +00:00
troytt 76d8fa3c8a feat(skills): implement 1.13c Poison Nova [Skill #092] (fixes #246) 2026-09-22 03:44:46 +00:00
troytt 4f6d8d44e9 feat(skill-092): implement 1.13c Poison Nova module and Vitest suite (#246) 2026-09-22 03:44:45 +00:00
troytt 2d9573805e feat(skills): implement 1.13c Lower Resist [Skill #091] (fixes #245) 2026-09-22 03:44:44 +00:00
troytt c2c139f17b feat(skill-091): implement 1.13c Lower Resist module and Vitest suite (#245) 2026-09-22 03:44:43 +00:00
troytt 192cdc2424 feat(skills): implement 1.13c IronGolem [Skill #090] (fixes #244) 2026-09-22 03:44:42 +00:00
troytt 8189f3c643 feat(skill-090): implement 1.13c IronGolem module and Vitest suite (#244) 2026-09-22 03:44:42 +00:00
troytt bec78e45ac feat(skills): implement 1.13c Summon Resist [Skill #089] (fixes #243) 2026-09-22 03:44:40 +00:00
troytt 494409e04a feat(skill-089): implement 1.13c Summon Resist module and Vitest suite (#243) 2026-09-22 03:44:40 +00:00
troytt a6d41e7c1b feat(skills): implement 1.13c Bone Prison [Skill #088] (fixes #242) 2026-09-22 03:44:39 +00:00
troytt 83bbb4abf6 feat(skill-088): implement 1.13c Bone Prison module and Vitest suite (#242) 2026-09-22 03:44:38 +00:00
troytt 24bc586278 feat(skills): implement 1.13c Decrepify [Skill #087] (fixes #241) 2026-09-22 03:44:37 +00:00
troytt d7993ebc3e feat(skill-087): implement 1.13c Decrepify module and Vitest suite (#241) 2026-09-22 03:44:37 +00:00
troytt 3243ef2c29 feat(skills): implement 1.13c Attract [Skill #086] (fixes #240) 2026-09-22 03:44:35 +00:00
troytt cf075924de feat(skill-086): implement 1.13c Attract module and Vitest suite (#240) 2026-09-22 03:44:35 +00:00
troytt 8b04d010fd feat(skills): implement 1.13c BloodGolem [Skill #085] (fixes #239) 2026-09-22 03:44:34 +00:00
troytt 130c22e563 feat(skill-085): implement 1.13c BloodGolem module and Vitest suite (#239) 2026-09-22 03:44:33 +00:00
50 changed files with 3825 additions and 0 deletions

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #085: BloodGolem (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 85 ("BloodGolem")
* - Class: "nec"
* - Required Level: 18
* - Server Functions: srvstfunc=0, srvdofunc=56, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon="BloodGolem"
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 85,
name: "BloodGolem",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 56,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-085-bloodgolem',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #086: Attract (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 86 ("Attract")
* - Class: "nec"
* - Required Level: 24
* - Server Functions: srvstfunc=18, srvdofunc=59, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 86,
name: "Attract",
charClass: "nec",
srvStFunc: 18,
srvDoFunc: 59,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-086-attract',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #087: Decrepify (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 87 ("Decrepify")
* - Class: "nec"
* - Required Level: 24
* - Server Functions: srvstfunc=0, srvdofunc=30, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 87,
name: "Decrepify",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 30,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-087-decrepify',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #088: Bone Prison (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 88 ("Bone Prison")
* - Class: "nec"
* - Required Level: 24
* - Server Functions: srvstfunc=19, srvdofunc=62, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon="bonewall"
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 88,
name: "Bone Prison",
charClass: "nec",
srvStFunc: 19,
srvDoFunc: 62,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-088-bone-prison',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #089: Summon Resist (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 89 ("Summon Resist")
* - Class: "nec"
* - Required Level: 24
* - Server Functions: srvstfunc=0, srvdofunc=0, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="summonresist", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 89,
name: "Summon Resist",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 0,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-089-summon-resist',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #090: IronGolem (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 90 ("IronGolem")
* - Class: "nec"
* - Required Level: 24
* - Server Functions: srvstfunc=20, srvdofunc=57, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="thorns", summon="IronGolem"
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 90,
name: "IronGolem",
charClass: "nec",
srvStFunc: 20,
srvDoFunc: 57,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-090-irongolem',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #091: Lower Resist (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 91 ("Lower Resist")
* - Class: "nec"
* - Required Level: 30
* - Server Functions: srvstfunc=0, srvdofunc=30, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 91,
name: "Lower Resist",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 30,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-091-lower-resist',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #092: Poison Nova (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 92 ("Poison Nova")
* - Class: "nec"
* - Required Level: 30
* - Server Functions: srvstfunc=0, srvdofunc=22, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="poisonnova", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="(skill('Poison Dagger'.blvl)+skill('Poison Explosion'.blvl))*par8", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 92,
name: "Poison Nova",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 22,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-092-poison-nova',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #093: Bone Spirit (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 93 ("Bone Spirit")
* - Class: "nec"
* - Required Level: 30
* - Server Functions: srvstfunc=0, srvdofunc=10, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="bonespirit", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="(skill('Bone Wall'.blvl)+skill('Bone Prison'.blvl)+skill('Teeth'.blvl)+skill('Bone Spear'.blvl))*par8", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 93,
name: "Bone Spirit",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 10,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-093-bone-spirit',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #094: FireGolem (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 94 ("FireGolem")
* - Class: "nec"
* - Required Level: 30
* - Server Functions: srvstfunc=0, srvdofunc=56, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon="FireGolem"
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 94,
name: "FireGolem",
charClass: "nec",
srvStFunc: 0,
srvDoFunc: 56,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-094-firegolem',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #095: Revive (NEC)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 95 ("Revive")
* - Class: "nec"
* - Required Level: 30
* - Server Functions: srvstfunc=21, srvdofunc=58, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 95,
name: "Revive",
charClass: "nec",
srvStFunc: 21,
srvDoFunc: 58,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-095-revive',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #096: Sacrifice (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 96 ("Sacrifice")
* - Class: "pal"
* - Required Level: 1
* - Server Functions: srvstfunc=29, srvdofunc=64, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 96,
name: "Sacrifice",
charClass: "pal",
srvStFunc: 29,
srvDoFunc: 64,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-096-sacrifice',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #097: Smite (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 97 ("Smite")
* - Class: "pal"
* - Required Level: 1
* - Server Functions: srvstfunc=0, srvdofunc=150, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 97,
name: "Smite",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 150,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-097-smite',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #098: Might (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 98 ("Might")
* - Class: "pal"
* - Required Level: 1
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="might", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 98,
name: "Might",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-098-might',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #099: Prayer (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 99 ("Prayer")
* - Class: "pal"
* - Required Level: 1
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="prayer", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 99,
name: "Prayer",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-099-prayer',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #100: Resist Fire (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 100 ("Resist Fire")
* - Class: "pal"
* - Required Level: 1
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="resistfire", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 100,
name: "Resist Fire",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-100-resist-fire',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #101: Holy Bolt (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 101 ("Holy Bolt")
* - Class: "pal"
* - Required Level: 6
* - Server Functions: srvstfunc=0, srvdofunc=0, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="holybolt", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="(skill('Blessed Hammer'.blvl)+skill('Fist of the Heavens'.blvl))*par8", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 101,
name: "Holy Bolt",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 0,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-101-holy-bolt',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #102: Holy Fire (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 102 ("Holy Fire")
* - Class: "pal"
* - Required Level: 6
* - Server Functions: srvstfunc=0, srvdofunc=66, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="holyfire", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="skill('Resist Fire'.blvl)*par8+skill('Salvation'.blvl)*par7", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 102,
name: "Holy Fire",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 66,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-102-holy-fire',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #103: Thorns (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 103 ("Thorns")
* - Class: "pal"
* - Required Level: 6
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="thorns", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 103,
name: "Thorns",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-103-thorns',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #104: Defiance (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 104 ("Defiance")
* - Class: "pal"
* - Required Level: 6
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="defiance", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 104,
name: "Defiance",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-104-defiance',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #105: Resist Cold (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 105 ("Resist Cold")
* - Class: "pal"
* - Required Level: 6
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="resistcold", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 105,
name: "Resist Cold",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-105-resist-cold',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #106: Zeal (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 106 ("Zeal")
* - Class: "pal"
* - Required Level: 12
* - Server Functions: srvstfunc=37, srvdofunc=13, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 106,
name: "Zeal",
charClass: "pal",
srvStFunc: 37,
srvDoFunc: 13,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-106-zeal',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #107: Charge (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 107 ("Charge")
* - Class: "pal"
* - Required Level: 12
* - Server Functions: srvstfunc=31, srvdofunc=67, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 107,
name: "Charge",
charClass: "pal",
srvStFunc: 31,
srvDoFunc: 67,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-107-charge',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #108: Blessed Aim (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 108 ("Blessed Aim")
* - Class: "pal"
* - Required Level: 12
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="blessedaim", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 108,
name: "Blessed Aim",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-108-blessed-aim',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,44 @@
/**
* Diablo II: Lord of Destruction v1.13c — Skill #109: Cleansing (PAL)
*
* Authoritative 1.13c Ground Truth (`Patch_D2.mpq` -> `data\\global\\excel\\Skills.txt`):
* - Skill ID: 109 ("Cleansing")
* - Class: "pal"
* - Required Level: 12
* - Server Functions: srvstfunc=0, srvdofunc=65, srvprgfunc1=0, srvprgfunc2=0, srvprgfunc3=0
* - Associated Missile / State / Summon: missile="", state="cleansing", summon=""
* - 1.13c Synergy Formulas: EDmgSymPerCalc="", DmgSymPerCalc="", ELenSymPerCalc=""
*/
import type {
SkillEvalContext,
SkillEvalResult,
SkillExecContext,
SkillExecOutcome,
SkillModule,
} from '../../types.ts'
import { evaluateSkillCore113c, executeSkillCore113c } from '../../registry.ts'
export const skillModule: SkillModule = {
skillId: 109,
name: "Cleansing",
charClass: "pal",
srvStFunc: 0,
srvDoFunc: 65,
evaluate(ctx: SkillEvalContext): SkillEvalResult {
return evaluateSkillCore113c(ctx)
},
executeDo(ctx: SkillExecContext): SkillExecOutcome {
const outcome = executeSkillCore113c(ctx)
return {
...outcome,
notes: [
...outcome.notes,
'skill_module:skill-109-cleansing',
],
}
},
}
export default skillModule

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #085: BloodGolem (NEC)
* Gitea Tracking Issue: #239
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-085-bloodgolem.ts'
describe('[Skill #085] BloodGolem (NEC) — 1.13c Parity (Issue #239)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(85)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(85)
expect(skillModule.name).toBe("BloodGolem")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(85, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(25)
expect(r1.manaCost256).toBe(6400)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(85, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(61)
expect(r10.manaCost256).toBe(15616)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(85, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(101)
expect(r20.manaCost256).toBe(25856)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(2925)
expect(r20.petMax).toBe(1)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(85)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(85, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(85, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 85,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(85)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #086: Attract (NEC)
* Gitea Tracking Issue: #240
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-086-attract.ts'
describe('[Skill #086] Attract (NEC) — 1.13c Parity (Issue #240)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(86)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(86)
expect(skillModule.name).toBe("Attract")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(86, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(17)
expect(r1.manaCost256).toBe(4352)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(86, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(17)
expect(r10.manaCost256).toBe(4352)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(86, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(17)
expect(r20.manaCost256).toBe(4352)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(2010)
expect(r20.radiusSubtiles).toBe(9)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(86)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(86, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(86, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 86,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(86)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #087: Decrepify (NEC)
* Gitea Tracking Issue: #241
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-087-decrepify.ts'
describe('[Skill #087] Decrepify (NEC) — 1.13c Parity (Issue #241)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(87)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(87)
expect(skillModule.name).toBe("Decrepify")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(87, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(11)
expect(r1.manaCost256).toBe(2816)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(87, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(11)
expect(r10.manaCost256).toBe(2816)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(87, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(11)
expect(r20.manaCost256).toBe(2816)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(385)
expect(r20.radiusSubtiles).toBe(6)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(87)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(87, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(87, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 87,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(87)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #088: Bone Prison (NEC)
* Gitea Tracking Issue: #242
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-088-bone-prison.ts'
describe('[Skill #088] Bone Prison (NEC) — 1.13c Parity (Issue #242)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(88)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(88)
expect(skillModule.name).toBe("Bone Prison")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(88, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(27)
expect(r1.manaCost256).toBe(6912)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(88, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(18)
expect(r10.manaCost256).toBe(4608)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(88, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(8)
expect(r20.manaCost256).toBe(2048)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(11425)
expect(r20.petMax).toBe(1)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(88)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(88, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(88, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 88,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(88)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #089: Summon Resist (NEC)
* Gitea Tracking Issue: #243
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-089-summon-resist.ts'
describe('[Skill #089] Summon Resist (NEC) — 1.13c Parity (Issue #243)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(89)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(89)
expect(skillModule.name).toBe("Summon Resist")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(89, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(44)
expect(r1.manaCost256).toBe(11264)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(89, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(17)
expect(r10.manaCost256).toBe(4352)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(89, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(1)
expect(r20.manaCost256).toBe(256)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(1445)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(89)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(89, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(89, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 89,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(89)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #090: IronGolem (NEC)
* Gitea Tracking Issue: #244
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-090-irongolem.ts'
describe('[Skill #090] IronGolem (NEC) — 1.13c Parity (Issue #244)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(90)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(90)
expect(skillModule.name).toBe("IronGolem")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(90, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(35)
expect(r1.manaCost256).toBe(8960)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(90, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(35)
expect(r10.manaCost256).toBe(8960)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(90, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(35)
expect(r20.manaCost256).toBe(8960)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(435)
expect(r20.petMax).toBe(1)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(90)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(90, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(90, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 90,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(90)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #091: Lower Resist (NEC)
* Gitea Tracking Issue: #245
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-091-lower-resist.ts'
describe('[Skill #091] Lower Resist (NEC) — 1.13c Parity (Issue #245)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(91)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(91)
expect(skillModule.name).toBe("Lower Resist")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(91, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(22)
expect(r1.manaCost256).toBe(5632)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(91, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(22)
expect(r10.manaCost256).toBe(5632)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(91, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(22)
expect(r20.manaCost256).toBe(5632)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(1450)
expect(r20.radiusSubtiles).toBe(26)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(91)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(91, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(91, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 91,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(91)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #092: Poison Nova (NEC)
* Gitea Tracking Issue: #246
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-092-poison-nova.ts'
describe('[Skill #092] Poison Nova (NEC) — 1.13c Parity (Issue #246)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(92)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(92)
expect(skillModule.name).toBe("Poison Nova")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(92, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(20)
expect(r1.manaCost256).toBe(5120)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(256)
expect(r1.maxElemDmg256).toBe(464)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(92, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(20)
expect(r10.manaCost256).toBe(5120)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(896)
expect(r10.maxElemDmg256).toBe(1104)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(92, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(20)
expect(r20.manaCost256).toBe(5120)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(2048)
expect(r20.maxElemDmg256).toBe(2256)
expect(r20.durationFrames).toBe(50)
expect(r20.radiusSubtiles).toBe(0)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(92)!
const synId = 73
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(92, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(200)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(92, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 92,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(92)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #093: Bone Spirit (NEC)
* Gitea Tracking Issue: #247
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-093-bone-spirit.ts'
describe('[Skill #093] Bone Spirit (NEC) — 1.13c Parity (Issue #247)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(93)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(93)
expect(skillModule.name).toBe("Bone Spirit")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(93, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(12)
expect(r1.manaCost256).toBe(3072)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(5120)
expect(r1.maxElemDmg256).toBe(7680)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(93, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(16)
expect(r10.manaCost256).toBe(4224)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(42496)
expect(r10.maxElemDmg256).toBe(47360)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(93, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(21)
expect(r20.manaCost256).toBe(5504)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(87040)
expect(r20.maxElemDmg256).toBe(94464)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(0)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(93)!
const synId = 78
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(93, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(120)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(93, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 93,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(93)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #094: FireGolem (NEC)
* Gitea Tracking Issue: #248
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-094-firegolem.ts'
describe('[Skill #094] FireGolem (NEC) — 1.13c Parity (Issue #248)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(94)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(94)
expect(skillModule.name).toBe("FireGolem")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(94, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(50)
expect(r1.manaCost256).toBe(12800)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(2560)
expect(r1.maxElemDmg256).toBe(6912)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(94, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(140)
expect(r10.manaCost256).toBe(35840)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(23808)
expect(r10.maxElemDmg256).toBe(30464)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(94, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(240)
expect(r20.manaCost256).toBe(61440)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(50432)
expect(r20.maxElemDmg256).toBe(59648)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(1925)
expect(r20.petMax).toBe(1)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(94)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(94, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(94, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 94,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(94)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #095: Revive (NEC)
* Gitea Tracking Issue: #249
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/nec/skill-095-revive.ts'
describe('[Skill #095] Revive (NEC) — 1.13c Parity (Issue #249)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(95)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(95)
expect(skillModule.name).toBe("Revive")
expect(skillModule.charClass).toBe("nec")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(95, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(45)
expect(r1.manaCost256).toBe(11520)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(95, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(45)
expect(r10.manaCost256).toBe(11520)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(95, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(45)
expect(r20.manaCost256).toBe(11520)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(0)
expect(r20.petMax).toBe(20)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(95)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(95, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(95, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "nec",
skillId: 95,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(95)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #096: Sacrifice (PAL)
* Gitea Tracking Issue: #250
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-096-sacrifice.ts'
describe('[Skill #096] Sacrifice (PAL) — 1.13c Parity (Issue #250)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(96)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(96)
expect(skillModule.name).toBe("Sacrifice")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(96, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(96, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(96, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(465)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(96)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(96, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(96, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 96,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(96)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #097: Smite (PAL)
* Gitea Tracking Issue: #251
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-097-smite.ts'
describe('[Skill #097] Smite (PAL) — 1.13c Parity (Issue #251)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(97)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(97)
expect(skillModule.name).toBe("Smite")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(97, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(2)
expect(r1.manaCost256).toBe(512)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(97, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(2)
expect(r10.manaCost256).toBe(512)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(97, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(2)
expect(r20.manaCost256).toBe(512)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(110)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(97)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(97, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(97, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 97,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(97)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #098: Might (PAL)
* Gitea Tracking Issue: #252
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-098-might.ts'
describe('[Skill #098] Might (PAL) — 1.13c Parity (Issue #252)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(98)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(98)
expect(skillModule.name).toBe("Might")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(98, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(1)
expect(r1.manaCost256).toBe(256)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(98, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(1)
expect(r10.manaCost256).toBe(256)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(98, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(1)
expect(r20.manaCost256).toBe(256)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(98)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(98, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(98, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 98,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(98)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #099: Prayer (PAL)
* Gitea Tracking Issue: #253
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-099-prayer.ts'
describe('[Skill #099] Prayer (PAL) — 1.13c Parity (Issue #253)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(99)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(99)
expect(skillModule.name).toBe("Prayer")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(99, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(1)
expect(r1.manaCost256).toBe(256)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(512)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(99, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(2)
expect(r10.manaCost256).toBe(688)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(2816)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(99, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(4)
expect(r20.manaCost256).toBe(1168)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(6400)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(99)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(99, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(99, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 99,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(99)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #100: Resist Fire (PAL)
* Gitea Tracking Issue: #254
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-100-resist-fire.ts'
describe('[Skill #100] Resist Fire (PAL) — 1.13c Parity (Issue #254)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(100)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(100)
expect(skillModule.name).toBe("Resist Fire")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(100, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(100, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(100, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(100)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(100, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(100, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 100,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(100)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #101: Holy Bolt (PAL)
* Gitea Tracking Issue: #255
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-101-holy-bolt.ts'
describe('[Skill #101] Holy Bolt (PAL) — 1.13c Parity (Issue #255)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(101)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(101)
expect(skillModule.name).toBe("Holy Bolt")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(101, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(2)
expect(r1.manaCost256).toBe(512)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(2048)
expect(r1.maxElemDmg256).toBe(4096)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(101, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(2)
expect(r10.manaCost256).toBe(656)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(21504)
expect(r10.maxElemDmg256).toBe(24064)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(101, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(3)
expect(r20.manaCost256).toBe(816)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(50176)
expect(r20.maxElemDmg256).toBe(56320)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(39)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(101)!
const synId = 112
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(101, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(1000)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(101, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 101,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(101)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #102: Holy Fire (PAL)
* Gitea Tracking Issue: #256
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-102-holy-fire.ts'
describe('[Skill #102] Holy Fire (PAL) — 1.13c Parity (Issue #256)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(102)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(102)
expect(skillModule.name).toBe("Holy Fire")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(102, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(256)
expect(r1.maxElemDmg256).toBe(768)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(102, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(1664)
expect(r10.maxElemDmg256).toBe(2176)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(102, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(4736)
expect(r20.maxElemDmg256).toBe(5248)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(25)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(102)!
const synId = 100
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(102, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(360)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(102, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 102,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(102)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #103: Thorns (PAL)
* Gitea Tracking Issue: #257
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-103-thorns.ts'
describe('[Skill #103] Thorns (PAL) — 1.13c Parity (Issue #257)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(103)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(103)
expect(skillModule.name).toBe("Thorns")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(103, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(103, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(103, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(103)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(103, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(103, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 103,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(103)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #104: Defiance (PAL)
* Gitea Tracking Issue: #258
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-104-defiance.ts'
describe('[Skill #104] Defiance (PAL) — 1.13c Parity (Issue #258)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(104)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(104)
expect(skillModule.name).toBe("Defiance")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(104, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(104, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(104, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(104)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(104, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(104, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 104,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(104)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #105: Resist Cold (PAL)
* Gitea Tracking Issue: #259
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-105-resist-cold.ts'
describe('[Skill #105] Resist Cold (PAL) — 1.13c Parity (Issue #259)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(105)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(105)
expect(skillModule.name).toBe("Resist Cold")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(105, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(105, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(105, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(105)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(105, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(105, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 105,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(105)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #106: Zeal (PAL)
* Gitea Tracking Issue: #260
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-106-zeal.ts'
describe('[Skill #106] Zeal (PAL) — 1.13c Parity (Issue #260)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(106)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(106)
expect(skillModule.name).toBe("Zeal")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(106, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(2)
expect(r1.manaCost256).toBe(512)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(106, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(2)
expect(r10.manaCost256).toBe(512)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(106, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(2)
expect(r20.manaCost256).toBe(512)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(0)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(106)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(106, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(106, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 106,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(106)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #107: Charge (PAL)
* Gitea Tracking Issue: #261
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-107-charge.ts'
describe('[Skill #107] Charge (PAL) — 1.13c Parity (Issue #261)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(107)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(107)
expect(skillModule.name).toBe("Charge")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(107, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(9)
expect(r1.manaCost256).toBe(2304)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(107, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(9)
expect(r10.manaCost256).toBe(2304)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(107, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(9)
expect(r20.manaCost256).toBe(2304)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(0)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(107)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(107, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(107, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 107,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(107)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #108: Blessed Aim (PAL)
* Gitea Tracking Issue: #262
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-108-blessed-aim.ts'
describe('[Skill #108] Blessed Aim (PAL) — 1.13c Parity (Issue #262)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(108)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(108)
expect(skillModule.name).toBe("Blessed Aim")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(108, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(108, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(108, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(108)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(108, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(108, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 108,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(108)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})

View File

@ -0,0 +1,109 @@
/**
* 1.13c Numerical & Runtime Verification Suite — Skill #109: Cleansing (PAL)
* Gitea Tracking Issue: #263
*/
import { describe, expect, it } from 'vitest'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { WorldArena } from '../../../src/game/engine/world-arena.ts'
import { skillModule } from '../../../src/game/skills/impl/pal/skill-109-cleansing.ts'
describe('[Skill #109] Cleansing (PAL) — 1.13c Parity (Issue #263)', () => {
it('evaluates exact 1.13c Skills.txt / Missiles.txt formulas at slvl 1, 10, and 20', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(109)!
expect(skillRec).toBeDefined()
expect(skillModule.skillId).toBe(109)
expect(skillModule.name).toBe("Cleansing")
expect(skillModule.charClass).toBe("pal")
const stats1 = new UnitStatList()
stats1.setBaseSkillLevel(109, 1)
const r1 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 1, blvl: 1, statList: stats1 })
expect(r1.manaCost).toBe(0)
expect(r1.manaCost256).toBe(0)
expect(r1.minPhysDmg).toBe(0)
expect(r1.maxPhysDmg).toBe(0)
expect(r1.minElemDmg256).toBe(0)
expect(r1.maxElemDmg256).toBe(0)
const stats10 = new UnitStatList()
stats10.setBaseSkillLevel(109, 10)
const r10 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 10, blvl: 10, statList: stats10 })
expect(r10.manaCost).toBe(0)
expect(r10.manaCost256).toBe(0)
expect(r10.minPhysDmg).toBe(0)
expect(r10.maxPhysDmg).toBe(0)
expect(r10.minElemDmg256).toBe(0)
expect(r10.maxElemDmg256).toBe(0)
const stats20 = new UnitStatList()
stats20.setBaseSkillLevel(109, 20)
const r20 = skillModule.evaluate!({ registry, skill: skillRec, slvl: 20, blvl: 20, statList: stats20 })
expect(r20.manaCost).toBe(0)
expect(r20.manaCost256).toBe(0)
expect(r20.minPhysDmg).toBe(0)
expect(r20.maxPhysDmg).toBe(0)
expect(r20.minElemDmg256).toBe(0)
expect(r20.maxElemDmg256).toBe(0)
expect(r20.durationFrames).toBe(0)
expect(r20.radiusSubtiles).toBe(54)
expect(r20.petMax).toBe(0)
expect(r20.cooldownFrames).toBe(0)
})
it('enforces 1.13c blvl synergy rule (hard points boost synergy; +skills and item charges do NOT)', async () => {
const registry = await getSharedDataRegistry()
const skillRec = registry.getSkillById(109)!
const synId = null
const statsBase = new UnitStatList()
statsBase.setBaseSkillLevel(109, 20)
if (synId !== null) {
statsBase.setBaseSkillLevel(synId, 20)
}
const withHardPoints = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsBase,
})
expect(withHardPoints.synergyBonusPct).toBe(0)
// +20 allskills and +33 Marrowwalk charges without hard points must yield 0 synergy
const statsSoftOnly = new UnitStatList()
statsSoftOnly.setBaseSkillLevel(109, 20)
statsSoftOnly.addStat('item_allskills', 20)
if (synId !== null) {
statsSoftOnly.setChargedSkillLevel(synId, 33)
}
const withSoftOnly = skillModule.evaluate!({
registry,
skill: skillRec,
slvl: 20,
blvl: 20,
statList: statsSoftOnly,
})
expect(withSoftOnly.synergyBonusPct).toBe(0)
})
it('executes runtime skill effect in WorldArena (castCount, action frame, and combat/state/missile/summon outcome)', async () => {
const registry = await getSharedDataRegistry()
const arena = new WorldArena({
registry,
classCode: "pal",
skillId: 109,
slvl: 20,
})
const outcome = arena.triggerCast()
arena.stepTicks(15)
const dbg = arena.getDebugState()
expect(dbg.ready).toBe(true)
expect(dbg.skillId).toBe(109)
expect(dbg.castCount).toBeGreaterThanOrEqual(1)
expect(dbg.actionFrameTriggered).toBe(true)
expect(outcome?.executed).toBe(true)
})
})