feat(skills): authentic 1.13c Cast Overlay system and elemental spell animations (Fixes #386)

This commit is contained in:
troytt 2026-09-23 03:53:57 +00:00
parent 7d7925ab2b
commit 6eaeab4619
43 changed files with 6191 additions and 14 deletions

View File

@ -0,0 +1,92 @@
## 暗黑破坏神 II (v1.13c) 施法视觉效果 (Cast Overlay) 底层调查与实现设计规范
### 一、 机制调查与 1.13c 真实底层数据 (Ground Truth Invariant)
在暗黑破坏神 II v1.13c 引擎体系(`D2Common.dll`、`D2Client.dll`)中,释放法术时角色脚下涌现并包裹全身的动画效果并非角色自身动作(Action Frame)或飞弹(Missile),而是属于 **Unit Overlay(单位覆盖动画系统)**。
#### 1. 数据驱动关系
1. **`Skills.txt`**:包含列 **`castoverlay`**,定义技能施展时触发的覆盖效果标识(例如技能 36 火弹绑定 `fire_cast_1`)。
2. **`Overlay.txt`**:集中定义所有覆盖效果的物理与渲染属性:
- **`Filename`**:指向 DCC 动画资源路径(位于 `d2data.mpq: data/global/overlays/{Filename}.dcc`)。
- **`Frames`**:动画总帧数。
- **`AnimRate`**:动画播放速率。法术施法特效原版均为 **16**(代表在 25 FPS 逻辑刻下,每 25 tick 播放 16 帧动画,即步长 `16 / 25 = 0.64` 帧/刻,总时长约 0.88 秒)。
- **`Trans`**:混合渲染模式。原版为 **3**(**Additive Blending / 增色叠加混合**),呈现火焰、电光与寒冰的自发光半透明视觉质感。
- **`PreDraw`**:渲染层级。原版均为 **0**(**Foreground / 前景层**),先绘制施法者角色精灵,再在前景叠加该 Overlay,形成火焰包裹躯干的立体层次。
- **`Xoffset` / `Yoffset` / `Height1..4`**:均为 **0**,严格以施法者单位脚底地面中心点 `(0, 0)` 为基准。
- **`InitRadius` / `Radius` / `Red, Green, Blue`**:动态点光源参数(半径均为 9 个子网格)。Overlay 激活期间会在角色周围投射对应属性的动态光晕。
---
### 二、 核心法术原版 Overlay 映射与 DCC 规格表
经解析 `samples/d2/Patch_D2.mpq` 与 `d2data.mpq`,法师三大元素系法术完整映射如下:
| 技能类别 | 技能名称 (Id) | `Skills.txt` `castoverlay` | DCC 文件名 (`data/global/overlays/`) | 帧数 | DCC 碰撞盒 (Left, Top, W, H) | 光照颜色 (R, G, B, Radius) | 视觉特征 |
| :--- | :--- | :--- | :--- | :---: | :--- | :--- | :--- |
| **火焰系** | **火弹 Fire Bolt (36)** | `fire_cast_1` | `FireCast_for_Sorceress.dcc` | 14 | `[-55, -127, 117, 164]` | (255, 178, 64, 9) | **从脚底升腾直窜头顶的火焰柱 (即 Issue 附件效果)** |
| **火焰系** | 火球 (47), 强化/火焰强化 (46), 火墙 (51), 陨石 (56), 九头海蛇 (62) | `fire_cast_2` | `FireCast2.dcc` | 16 | `[-74, -89, 145, 133]` | (255, 178, 64, 9) | 范围更大、旋转包裹的炽热烈焰 |
| **冰霜系** | 冰弹 (39), 冰封甲 (40), 冰风暴 (45) | `ice_cast_1` | `IceCastNew01.dcc` | 15 | `[-49, -90, 97, 55]` | (81, 81, 255, 9) | 脚下微弱旋起的寒冰碎屑与白霜冷雾 |
| **冰霜系** | 霜之新星 (44), 碎冰甲 (50), 冰尖柱 (55) | `ice_cast_2` | `IceCastNew02.dcc` | 15 | `[-58, -100, 115, 123]` | (81, 81, 255, 9) | 向上旋转升腾的深蓝冰柱与霜雾 |
| **冰霜系** | 暴风雪 (59), 寒冰甲 (60), 冰封球 (64) | `ice_cast_3` | `IceCastNew03.dcc` | 16 | `[-63, -113, 127, 148]` | (81, 81, 255, 9) | 强烈的暴风雪霜华与立体冰凌环绕 |
| **闪电系** | 充能弹 (38), 静电场 (42), 新星 (48), 闪电 (49), 连锁闪电 (53) | `light_cast_1` | `LightningCast.dcc` | 10 | `[-79, -107, 164, 144]` | (255, 255, 255, 9) | 脚底向外炸裂并包裹全身的电弧跳跃 |
| **闪电系** | 心灵传动 (43), 雷云风暴 (57), 能量护盾 (58) | `light_cast_2` | `LightningCastRunesFront.dcc` | 10 | `[-73, -154, 147, 190]` | (255, 255, 255, 9) | 闪电符文闪耀与高耸电光柱 |
| **特殊** | 传送 Teleport (54) | `teleport` | `Teleport.dcc` | 18 | `[-61, -108, 136, 154]` | (255, 255, 200, 5) | 金黄色空间坍缩与残影粒子 |
---
### 三、 系统架构与实现方案 (diablo2-web)
遵循项目 **Zero Runtime MPQ Invariant** 及 **Anti-Silent Failures** 原则:
#### 1. 离线资源烘焙流水线 (`scripts/pack-overlays.ts`)
- 提取 `d2data.mpq` 中上述 8 个关键施法 Overlay DCC 精灵文件。
- 读取 `data/global/palette/ACT1/pal.pl2` 进行真实调色板色彩映射。
- 将单向动画打包成 PNG 纹理图集并导出 JSON 元数据至 `public/overlays/` 与 `samples/d2-packs/overlays/`。
- 自动生成类型定义 `src/render/overlays-meta.ts`,提供精确的 `box.left, box.top, width, height, animRate, trans` 常量。
#### 2. 运行时数据契约与实体抽象 (`src/game/skills.ts` & `src/game/engine.ts`)
- 扩展 `SkillDef` 接口,包含 `castOverlay?: string`。
- 建立 `CANONICAL_113C_OVERLAYS` 真值表,包含 overlay 标识到对应资源与物理属性的映射。
- 在 `GameEngine` 中引入 `ActiveOverlay` 活跃实体管理:
```typescript
export interface ActiveOverlay {
readonly id: string
readonly overlayName: string
readonly casterId: string
x: number
y: number
frame: number
readonly maxFrames: number
readonly animRate: number
readonly preDraw: boolean
readonly trans: number
readonly lightRadius: number
readonly lightColor: readonly [number, number, number]
expired: boolean
}
```
- **生命周期规则**:
- 玩家或怪物在 `executePlayerSkillCast` 触发施法动作时,根据技能 `castoverlay` 生成 `ActiveOverlay`。
- 每个逻辑 tick(25 FPS):`overlay.frame += overlay.animRate / 25`。
- 当 `Math.floor(overlay.frame) >= overlay.maxFrames` 时,标记 `expired = true` 并在 tick 结束时销毁(单次播放不循环)。
- Overlay 坐标实时同步施法者当前脚下位置 `(caster.x, caster.y)`。
#### 3. 渲染管线集成 (`src/scene/act-scene.ts`)
- 资源载入:通过 `loadOverlayArtMap` 预载 `overlays/*.png`。
- 深度层级(Z-Order):
- 角色绘制完成后、飞弹绘制前,执行前景 Overlay 渲染。
- 混合模式(Blend Mode):
- 遇到 `trans === 3`,切换 Canvas2D `ctx.globalCompositeOperation = 'lighter'` 或 WebGL Additive 混合状态,绘制完成后恢复 `source-over`。
- 锚点投影(Anchor Projection):
- 精确应用 DCC 包围盒偏移:`drawX = overlay.x + frameMeta.anchorX`,`drawY = overlay.y + frameMeta.anchorY`。
- 动态光照反馈:
- 在 Overlay 存活期间,将其注册为动态点光源(如火焰投射 9 码的 RGB(255, 178, 64) 暖黄光晕)。
---
### 四、 验证与回归测试策略
1. **表格对齐测试**:验证 `parseSkillsTxt` 与 `Skills.txt`、`Overlay.txt` 的 `castoverlay` 字段及参数完全一致。
2. **生命周期测试**:验证 14 帧的 `fire_cast_1` 在 25 FPS 模拟下,在第 22 个 tick 精确结束并移除。
3. **渲染与图集测试**:验证 `FireCast_for_Sorceress`、`IceCastNew01`、`LightningCast` 纹理图集无缺失、锚点边界无裁切、Additive 模式下正确渲染。
4. **全场景稳定性**:自动化 Headless 运行,保障全 136 个关卡环境施法 Overlay 无跨法术贴图污染或未定义崩溃。

View File

@ -0,0 +1,156 @@
{
"name": "fire_cast_2",
"celFile": "FireCast2",
"width": 1024,
"height": 401,
"directions": 1,
"frames": 16,
"framesPerDirection": 16,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
178,
64
],
"box": {
"left": -74,
"top": -89,
"width": 145,
"height": 133
},
"groups": [
[
[
0,
0,
145,
133,
-74,
-89
],
[
146,
0,
145,
133,
-74,
-89
],
[
292,
0,
145,
133,
-74,
-89
],
[
438,
0,
145,
133,
-74,
-89
],
[
584,
0,
145,
133,
-74,
-89
],
[
730,
0,
145,
133,
-74,
-89
],
[
876,
0,
145,
133,
-74,
-89
],
[
0,
134,
145,
133,
-74,
-89
],
[
146,
134,
145,
133,
-74,
-89
],
[
292,
134,
145,
133,
-74,
-89
],
[
438,
134,
145,
133,
-74,
-89
],
[
584,
134,
145,
133,
-74,
-89
],
[
730,
134,
145,
133,
-74,
-89
],
[
876,
134,
145,
133,
-74,
-89
],
[
0,
268,
145,
133,
-74,
-89
],
[
146,
268,
145,
133,
-74,
-89
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,140 @@
{
"name": "fire_cast_1",
"celFile": "FireCast_for_Sorceress",
"width": 1024,
"height": 329,
"directions": 1,
"frames": 14,
"framesPerDirection": 14,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
178,
64
],
"box": {
"left": -55,
"top": -127,
"width": 117,
"height": 164
},
"groups": [
[
[
0,
0,
117,
164,
-55,
-127
],
[
118,
0,
117,
164,
-55,
-127
],
[
236,
0,
117,
164,
-55,
-127
],
[
354,
0,
117,
164,
-55,
-127
],
[
472,
0,
117,
164,
-55,
-127
],
[
590,
0,
117,
164,
-55,
-127
],
[
708,
0,
117,
164,
-55,
-127
],
[
826,
0,
117,
164,
-55,
-127
],
[
0,
165,
117,
164,
-55,
-127
],
[
118,
165,
117,
164,
-55,
-127
],
[
236,
165,
117,
164,
-55,
-127
],
[
354,
165,
117,
164,
-55,
-127
],
[
472,
165,
117,
164,
-55,
-127
],
[
590,
165,
117,
164,
-55,
-127
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,148 @@
{
"name": "ice_cast_1",
"celFile": "IceCastNew01",
"width": 512,
"height": 167,
"directions": 1,
"frames": 15,
"framesPerDirection": 15,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
81,
81,
255
],
"box": {
"left": -49,
"top": -90,
"width": 97,
"height": 55
},
"groups": [
[
[
0,
0,
97,
55,
-49,
-90
],
[
98,
0,
97,
55,
-49,
-90
],
[
196,
0,
97,
55,
-49,
-90
],
[
294,
0,
97,
55,
-49,
-90
],
[
392,
0,
97,
55,
-49,
-90
],
[
0,
56,
97,
55,
-49,
-90
],
[
98,
56,
97,
55,
-49,
-90
],
[
196,
56,
97,
55,
-49,
-90
],
[
294,
56,
97,
55,
-49,
-90
],
[
392,
56,
97,
55,
-49,
-90
],
[
0,
112,
97,
55,
-49,
-90
],
[
98,
112,
97,
55,
-49,
-90
],
[
196,
112,
97,
55,
-49,
-90
],
[
294,
112,
97,
55,
-49,
-90
],
[
392,
112,
97,
55,
-49,
-90
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,148 @@
{
"name": "ice_cast_2",
"celFile": "IceCastNew02",
"width": 512,
"height": 495,
"directions": 1,
"frames": 15,
"framesPerDirection": 15,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
81,
81,
255
],
"box": {
"left": -58,
"top": -100,
"width": 115,
"height": 123
},
"groups": [
[
[
0,
0,
115,
123,
-58,
-100
],
[
116,
0,
115,
123,
-58,
-100
],
[
232,
0,
115,
123,
-58,
-100
],
[
348,
0,
115,
123,
-58,
-100
],
[
0,
124,
115,
123,
-58,
-100
],
[
116,
124,
115,
123,
-58,
-100
],
[
232,
124,
115,
123,
-58,
-100
],
[
348,
124,
115,
123,
-58,
-100
],
[
0,
248,
115,
123,
-58,
-100
],
[
116,
248,
115,
123,
-58,
-100
],
[
232,
248,
115,
123,
-58,
-100
],
[
348,
248,
115,
123,
-58,
-100
],
[
0,
372,
115,
123,
-58,
-100
],
[
116,
372,
115,
123,
-58,
-100
],
[
232,
372,
115,
123,
-58,
-100
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,156 @@
{
"name": "ice_cast_3",
"celFile": "IceCastNew03",
"width": 1024,
"height": 297,
"directions": 1,
"frames": 16,
"framesPerDirection": 16,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
81,
81,
255
],
"box": {
"left": -63,
"top": -113,
"width": 127,
"height": 148
},
"groups": [
[
[
0,
0,
127,
148,
-63,
-113
],
[
128,
0,
127,
148,
-63,
-113
],
[
256,
0,
127,
148,
-63,
-113
],
[
384,
0,
127,
148,
-63,
-113
],
[
512,
0,
127,
148,
-63,
-113
],
[
640,
0,
127,
148,
-63,
-113
],
[
768,
0,
127,
148,
-63,
-113
],
[
896,
0,
127,
148,
-63,
-113
],
[
0,
149,
127,
148,
-63,
-113
],
[
128,
149,
127,
148,
-63,
-113
],
[
256,
149,
127,
148,
-63,
-113
],
[
384,
149,
127,
148,
-63,
-113
],
[
512,
149,
127,
148,
-63,
-113
],
[
640,
149,
127,
148,
-63,
-113
],
[
768,
149,
127,
148,
-63,
-113
],
[
896,
149,
127,
148,
-63,
-113
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,108 @@
{
"name": "light_cast_1",
"celFile": "LightningCast",
"width": 1024,
"height": 289,
"directions": 1,
"frames": 10,
"framesPerDirection": 10,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
255,
255
],
"box": {
"left": -79,
"top": -107,
"width": 164,
"height": 144
},
"groups": [
[
[
0,
0,
164,
144,
-79,
-107
],
[
165,
0,
164,
144,
-79,
-107
],
[
330,
0,
164,
144,
-79,
-107
],
[
495,
0,
164,
144,
-79,
-107
],
[
660,
0,
164,
144,
-79,
-107
],
[
825,
0,
164,
144,
-79,
-107
],
[
0,
145,
164,
144,
-79,
-107
],
[
165,
145,
164,
144,
-79,
-107
],
[
330,
145,
164,
144,
-79,
-107
],
[
495,
145,
164,
144,
-79,
-107
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,108 @@
{
"name": "light_cast_2",
"celFile": "LightningCastRunesFront",
"width": 1024,
"height": 381,
"directions": 1,
"frames": 10,
"framesPerDirection": 10,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
255,
255
],
"box": {
"left": -73,
"top": -154,
"width": 147,
"height": 190
},
"groups": [
[
[
0,
0,
147,
190,
-73,
-154
],
[
148,
0,
147,
190,
-73,
-154
],
[
296,
0,
147,
190,
-73,
-154
],
[
444,
0,
147,
190,
-73,
-154
],
[
592,
0,
147,
190,
-73,
-154
],
[
740,
0,
147,
190,
-73,
-154
],
[
0,
191,
147,
190,
-73,
-154
],
[
148,
191,
147,
190,
-73,
-154
],
[
296,
191,
147,
190,
-73,
-154
],
[
444,
191,
147,
190,
-73,
-154
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,172 @@
{
"name": "teleport",
"celFile": "Teleport",
"width": 1024,
"height": 464,
"directions": 1,
"frames": 18,
"framesPerDirection": 18,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 5,
"lightColor": [
255,
255,
200
],
"box": {
"left": -61,
"top": -108,
"width": 136,
"height": 154
},
"groups": [
[
[
0,
0,
136,
154,
-61,
-108
],
[
137,
0,
136,
154,
-61,
-108
],
[
274,
0,
136,
154,
-61,
-108
],
[
411,
0,
136,
154,
-61,
-108
],
[
548,
0,
136,
154,
-61,
-108
],
[
685,
0,
136,
154,
-61,
-108
],
[
822,
0,
136,
154,
-61,
-108
],
[
0,
155,
136,
154,
-61,
-108
],
[
137,
155,
136,
154,
-61,
-108
],
[
274,
155,
136,
154,
-61,
-108
],
[
411,
155,
136,
154,
-61,
-108
],
[
548,
155,
136,
154,
-61,
-108
],
[
685,
155,
136,
154,
-61,
-108
],
[
822,
155,
136,
154,
-61,
-108
],
[
0,
310,
136,
154,
-61,
-108
],
[
137,
310,
136,
154,
-61,
-108
],
[
274,
310,
136,
154,
-61,
-108
],
[
411,
310,
136,
154,
-61,
-108
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,140 @@
{
"name": "fire_cast_1",
"celFile": "FireCast_for_Sorceress",
"width": 1024,
"height": 329,
"directions": 1,
"frames": 14,
"framesPerDirection": 14,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
178,
64
],
"box": {
"left": -55,
"top": -127,
"width": 117,
"height": 164
},
"groups": [
[
[
0,
0,
117,
164,
-55,
-127
],
[
118,
0,
117,
164,
-55,
-127
],
[
236,
0,
117,
164,
-55,
-127
],
[
354,
0,
117,
164,
-55,
-127
],
[
472,
0,
117,
164,
-55,
-127
],
[
590,
0,
117,
164,
-55,
-127
],
[
708,
0,
117,
164,
-55,
-127
],
[
826,
0,
117,
164,
-55,
-127
],
[
0,
165,
117,
164,
-55,
-127
],
[
118,
165,
117,
164,
-55,
-127
],
[
236,
165,
117,
164,
-55,
-127
],
[
354,
165,
117,
164,
-55,
-127
],
[
472,
165,
117,
164,
-55,
-127
],
[
590,
165,
117,
164,
-55,
-127
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,156 @@
{
"name": "fire_cast_2",
"celFile": "FireCast2",
"width": 1024,
"height": 401,
"directions": 1,
"frames": 16,
"framesPerDirection": 16,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
178,
64
],
"box": {
"left": -74,
"top": -89,
"width": 145,
"height": 133
},
"groups": [
[
[
0,
0,
145,
133,
-74,
-89
],
[
146,
0,
145,
133,
-74,
-89
],
[
292,
0,
145,
133,
-74,
-89
],
[
438,
0,
145,
133,
-74,
-89
],
[
584,
0,
145,
133,
-74,
-89
],
[
730,
0,
145,
133,
-74,
-89
],
[
876,
0,
145,
133,
-74,
-89
],
[
0,
134,
145,
133,
-74,
-89
],
[
146,
134,
145,
133,
-74,
-89
],
[
292,
134,
145,
133,
-74,
-89
],
[
438,
134,
145,
133,
-74,
-89
],
[
584,
134,
145,
133,
-74,
-89
],
[
730,
134,
145,
133,
-74,
-89
],
[
876,
134,
145,
133,
-74,
-89
],
[
0,
268,
145,
133,
-74,
-89
],
[
146,
268,
145,
133,
-74,
-89
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,148 @@
{
"name": "ice_cast_1",
"celFile": "IceCastNew01",
"width": 512,
"height": 167,
"directions": 1,
"frames": 15,
"framesPerDirection": 15,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
81,
81,
255
],
"box": {
"left": -49,
"top": -90,
"width": 97,
"height": 55
},
"groups": [
[
[
0,
0,
97,
55,
-49,
-90
],
[
98,
0,
97,
55,
-49,
-90
],
[
196,
0,
97,
55,
-49,
-90
],
[
294,
0,
97,
55,
-49,
-90
],
[
392,
0,
97,
55,
-49,
-90
],
[
0,
56,
97,
55,
-49,
-90
],
[
98,
56,
97,
55,
-49,
-90
],
[
196,
56,
97,
55,
-49,
-90
],
[
294,
56,
97,
55,
-49,
-90
],
[
392,
56,
97,
55,
-49,
-90
],
[
0,
112,
97,
55,
-49,
-90
],
[
98,
112,
97,
55,
-49,
-90
],
[
196,
112,
97,
55,
-49,
-90
],
[
294,
112,
97,
55,
-49,
-90
],
[
392,
112,
97,
55,
-49,
-90
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,148 @@
{
"name": "ice_cast_2",
"celFile": "IceCastNew02",
"width": 512,
"height": 495,
"directions": 1,
"frames": 15,
"framesPerDirection": 15,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
81,
81,
255
],
"box": {
"left": -58,
"top": -100,
"width": 115,
"height": 123
},
"groups": [
[
[
0,
0,
115,
123,
-58,
-100
],
[
116,
0,
115,
123,
-58,
-100
],
[
232,
0,
115,
123,
-58,
-100
],
[
348,
0,
115,
123,
-58,
-100
],
[
0,
124,
115,
123,
-58,
-100
],
[
116,
124,
115,
123,
-58,
-100
],
[
232,
124,
115,
123,
-58,
-100
],
[
348,
124,
115,
123,
-58,
-100
],
[
0,
248,
115,
123,
-58,
-100
],
[
116,
248,
115,
123,
-58,
-100
],
[
232,
248,
115,
123,
-58,
-100
],
[
348,
248,
115,
123,
-58,
-100
],
[
0,
372,
115,
123,
-58,
-100
],
[
116,
372,
115,
123,
-58,
-100
],
[
232,
372,
115,
123,
-58,
-100
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,156 @@
{
"name": "ice_cast_3",
"celFile": "IceCastNew03",
"width": 1024,
"height": 297,
"directions": 1,
"frames": 16,
"framesPerDirection": 16,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
81,
81,
255
],
"box": {
"left": -63,
"top": -113,
"width": 127,
"height": 148
},
"groups": [
[
[
0,
0,
127,
148,
-63,
-113
],
[
128,
0,
127,
148,
-63,
-113
],
[
256,
0,
127,
148,
-63,
-113
],
[
384,
0,
127,
148,
-63,
-113
],
[
512,
0,
127,
148,
-63,
-113
],
[
640,
0,
127,
148,
-63,
-113
],
[
768,
0,
127,
148,
-63,
-113
],
[
896,
0,
127,
148,
-63,
-113
],
[
0,
149,
127,
148,
-63,
-113
],
[
128,
149,
127,
148,
-63,
-113
],
[
256,
149,
127,
148,
-63,
-113
],
[
384,
149,
127,
148,
-63,
-113
],
[
512,
149,
127,
148,
-63,
-113
],
[
640,
149,
127,
148,
-63,
-113
],
[
768,
149,
127,
148,
-63,
-113
],
[
896,
149,
127,
148,
-63,
-113
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,108 @@
{
"name": "light_cast_1",
"celFile": "LightningCast",
"width": 1024,
"height": 289,
"directions": 1,
"frames": 10,
"framesPerDirection": 10,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
255,
255
],
"box": {
"left": -79,
"top": -107,
"width": 164,
"height": 144
},
"groups": [
[
[
0,
0,
164,
144,
-79,
-107
],
[
165,
0,
164,
144,
-79,
-107
],
[
330,
0,
164,
144,
-79,
-107
],
[
495,
0,
164,
144,
-79,
-107
],
[
660,
0,
164,
144,
-79,
-107
],
[
825,
0,
164,
144,
-79,
-107
],
[
0,
145,
164,
144,
-79,
-107
],
[
165,
145,
164,
144,
-79,
-107
],
[
330,
145,
164,
144,
-79,
-107
],
[
495,
145,
164,
144,
-79,
-107
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,108 @@
{
"name": "light_cast_2",
"celFile": "LightningCastRunesFront",
"width": 1024,
"height": 381,
"directions": 1,
"frames": 10,
"framesPerDirection": 10,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 9,
"lightColor": [
255,
255,
255
],
"box": {
"left": -73,
"top": -154,
"width": 147,
"height": 190
},
"groups": [
[
[
0,
0,
147,
190,
-73,
-154
],
[
148,
0,
147,
190,
-73,
-154
],
[
296,
0,
147,
190,
-73,
-154
],
[
444,
0,
147,
190,
-73,
-154
],
[
592,
0,
147,
190,
-73,
-154
],
[
740,
0,
147,
190,
-73,
-154
],
[
0,
191,
147,
190,
-73,
-154
],
[
148,
191,
147,
190,
-73,
-154
],
[
296,
191,
147,
190,
-73,
-154
],
[
444,
191,
147,
190,
-73,
-154
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,172 @@
{
"name": "teleport",
"celFile": "Teleport",
"width": 1024,
"height": 464,
"directions": 1,
"frames": 18,
"framesPerDirection": 18,
"animRate": 16,
"trans": 3,
"preDraw": false,
"lightRadius": 5,
"lightColor": [
255,
255,
200
],
"box": {
"left": -61,
"top": -108,
"width": 136,
"height": 154
},
"groups": [
[
[
0,
0,
136,
154,
-61,
-108
],
[
137,
0,
136,
154,
-61,
-108
],
[
274,
0,
136,
154,
-61,
-108
],
[
411,
0,
136,
154,
-61,
-108
],
[
548,
0,
136,
154,
-61,
-108
],
[
685,
0,
136,
154,
-61,
-108
],
[
822,
0,
136,
154,
-61,
-108
],
[
0,
155,
136,
154,
-61,
-108
],
[
137,
155,
136,
154,
-61,
-108
],
[
274,
155,
136,
154,
-61,
-108
],
[
411,
155,
136,
154,
-61,
-108
],
[
548,
155,
136,
154,
-61,
-108
],
[
685,
155,
136,
154,
-61,
-108
],
[
822,
155,
136,
154,
-61,
-108
],
[
0,
310,
136,
154,
-61,
-108
],
[
137,
310,
136,
154,
-61,
-108
],
[
274,
310,
136,
154,
-61,
-108
],
[
411,
310,
136,
154,
-61,
-108
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,295 @@
overlay Filename version Frames Character PreDraw 1ofN Dir Open Beta Xoffset Yoffset Height1 Height2 Height3 Height4 AnimRate LoopWaitTime Trans InitRadius Radius Red Green Blue NumDirections LocalBlood
null null 0 2 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
lightning ShockHitSm 0 20 ? 0 1 0 1 1 0 0 0 0 0 0 16 0 3 11 11 255 255 255 1 0
cast_resistfire AuraResistFireFrontCast 0 11 Paladin 1 1 0 1 1 0 0 0 0 0 0 16 0 3 1 10 255 193 103 1 0
aura_resistfire AuraResistFireFront 0 20 Paladin 1 1 0 1 1 0 0 0 0 0 0 16 0 3 6 6 255 193 103 1 0
cast_resistcold AuraResistColdBackCast 0 11 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 10 210 210 255 1 0
aura_resistcold AuraResistColdBack 0 20 Paladin 1 1 0 1 1 0 0 0 0 0 0 16 0 3 6 6 210 210 255 1 0
cast_resistlight AuraResistLightningBackCast 0 11 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 10 255 255 200 1 0
aura_resistlight AuraResistLightningBack 0 20 Paladin 1 1 0 1 1 0 0 0 0 0 0 16 0 3 6 6 255 255 200 1 0
cast_resistall AuraResistAllCast 0 20 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 1 10 211 148 255 1 0
aura_resistall_front AuraResistAllFront 0 20 Paladin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 6 6 211 148 255 1 0
aura_resistall_back AuraResistAllBack 0 20 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 6 6 211 148 255 1 0
aura_might_front AuraMightFront 0 15 Paladin 0 1 0 1 1 4 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
aura_might_back AuraMightBack 0 15 Paladin 1 1 0 1 1 4 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
aura_prayer_front AuraPrayerFront 0 16 Paladin 0 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
aura_prayer_back AuraPrayerBack 0 16 Paladin 1 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
aura_holyfire_front AuraHolyFireFront 0 12 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
aura_holyfire_back AuraHolyFireBack 0 12 Paladin 1 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hit_holyfire AuraHolyFireHit 0 12 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
target_holyfire AuraHfireIndicate 0 19 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
aura_thorns_front AuraThornsFront 0 18 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
aura_thorns_back AuraThornsBack 0 18 Paladin 1 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hit_thorns AuraThornsHit 0 10 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
target_thorns AuraHfireIndicate 0 19 Paladin 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
aura_defiance_front AuraDefianceFront 0 15 Paladin 0 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
aura_defiance_back AuraDefianceBack 0 15 Paladin 1 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
aura_fanatic AuraFanatic 0 20 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
healing OverlayH 0 17 ? 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
receiving AmazonHealSpell 0 20 Amazon 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 8 255 184 199 1 0
cast_undead HolyBoltCloud 0 16 ? 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
fire_cast_1 FireCast_for_Sorceress 0 14 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 255 178 64 1 0
fire_cast_2 FireCast2 0 16 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 255 178 64 1 0
ice_cast_1 IceCastNew01 0 15 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 81 81 255 1 0
ice_cast_2 IceCastNew02 0 15 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 81 81 255 1 0
ice_cast_3 IceCastNew03 0 15 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 81 81 255 1 0
light_cast_1 LightningCast 0 10 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 255 255 255 1 0
fire_explode FireExplode 0 12 ? 0 1 0 1 1 0 0 0 0 0 0 16 0 3 14 14 255 193 103 1 0
ice_explode IceExplode 0 16 ? 0 1 0 1 1 0 0 0 0 0 0 16 0 3 14 14 210 210 255 1 0
warmth Warmth 0 14 ? 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 8 255 193 103 1 0
frozenarmor FrozenArmor 0 24 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 13 200 200 255 1 0
shiverarmor FrozenArmor 0 24 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 13 200 200 255 1 0
chillarmor FrozenArmor 0 24 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 13 200 200 255 1 0
cast_shivers AuraResistColdCast 0 11 Sorceress 1 1 0 0 0 0 0 14 0 -14 -60 24 0 3 1 6 81 81 255 1 0
frozenarmor_hit AuraResistColdCast 0 11 Sorceress 1 1 0 0 0 0 0 14 0 -14 -60 24 0 3 1 6 81 81 255 1 0
shiverarmor_hit AuraResistColdCast 0 11 Sorceress 1 1 0 1 1 0 0 14 0 -14 -60 24 0 3 1 6 81 81 255 1 0
chillarmor_hit AuraResistColdCast 0 11 Sorceress 1 1 0 0 0 0 0 14 0 -14 -60 24 0 3 1 6 81 81 255 1 0
teleport Teleport 0 18 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 1 5 255 255 200 1 0
srfirehit SRFireHit 0 16 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 193 103 1 0
cast_innersight EOZCast 0 29 Amazon 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 6 255 255 255 1 0
innersight HardenArmor 0 24 Amazon 0 1 0 1 1 0 0 0 0 0 0 16 0 3 6 6 255 255 255 1 0
cast_slowmissiles HOAcast 0 21 Amazon 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 6 255 255 255 1 0
handofathena HOAmissileExplode 0 9 Amazon 0 1 0 0 0 0 0 0 0 0 0 16 0 3 2 2 255 255 255 1 0
cast_fistofares FOACast 0 20 Amazon 0 1 0 0 0 0 0 0 0 0 0 16 0 3 1 6 255 255 255 1 0
durieldead_fwd DurielBloodsplatOverlayFwd 0 26 ? 1 1 0 0 0 0 0 0 0 0 0 8 0 5 0 0 255 255 255 1 1
cast_familiar FamiliarCast 0 21 Necro 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
doubledamage1 impacting01 0 4 all 0 3 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
doubledamage2 impacting02 0 4 all 0 2 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
doubledamage3 impacting03 0 4 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
shrine_shimmer00 ShrineShimmer00 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_shimmer01 ShrineShimmer01 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_armor ShrineArmor 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_combat ShrineCombat 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_resist_lightning ShrineResistLightning 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_resist_fire ShrineResistFire 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_resist_cold ShrineResistCold 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_resist_poison ShrineResistPoison 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_skill ShrineSkill 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_mana_regen ShrineManaRegen 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_stamina ShrineStamina 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
shrine_experience ShrineExperience 0 15 all 0 1 0 1 1 0 0 14 0 -14 -60 8 0 3 0 0 255 255 255 1 0
curse_hit CurseHit 0 10 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 10 255 193 103 1 0
itemgleam Gleam 0 8 item 0 1 0 1 1 0 0 0 0 0 0 16 4000 3 0 0 255 255 255 1 0
multigleam multigleam 0 10 objects 0 1 0 0 0 0 0 0 0 0 0 10 4000 3 0 0 255 255 255 1 0
npcalert NPCSpeechBalloon 0 16 npcs 0 1 0 1 1 -5 -7 -30 0 0 -50 9 7000 3 0 0 255 255 255 1 0
gleamtest3 Gleam 0 8 test 0 1 0 1 1 10 -14 0 0 0 0 11 4000 3 0 0 255 255 255 1 0
gleamtest4 Gleam 0 8 test 0 1 0 1 1 -10 -12 0 0 0 0 12 4000 3 0 0 255 255 255 1 0
shoutstart ShoutStart 0 20 all 0 1 0 1 1 0 0 0 0 0 0 8 0 3 1 6 255 255 255 1 0
shout Shout 0 20 all 0 1 0 1 1 0 0 0 0 0 0 8 0 3 1 6 255 255 255 1 0
taunt CurseConfuseEffect 0 24 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 6 255 193 103 1 0
zakarumpriest_death Zakarum_Priest_DeathGlow 0 24 all 0 1 0 1 1 0 0 0 0 0 0 8 0 3 2 2 103 193 255 1 0
thornedhulk_death ThornedHulkDeathOverlay 0 25 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 2 2 255 193 103 1 0
dust BAJumpLandPuff01 0 8 all 0 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
fire_hit FireHit 0 8 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 3 1 255 193 103 1 0
frogdemon_death FrogExplode 0 14 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
fetish_aura FetishFreakOut 0 16 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
brraisefront brraisedeadfront 0 16 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
brraiseback brraisedeadback 0 16 all 1 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
energyshield ManaShieldBall 0 8 Sorceress 0 1 0 0 0 0 0 0 0 0 0 8 0 5 0 0 255 255 255 1 0
energyshieldhit0 ManaShieldDir00 0 9 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit1 ManaShieldDir02 0 9 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit2 ManaShieldDir04 0 9 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit3 ManaShieldDir06 0 9 Sorceress 1 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit4 ManaShieldDir08 0 9 Sorceress 1 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit5 ManaShieldDir10 0 9 Sorceress 1 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit6 ManaShieldDir12 0 9 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
energyshieldhit7 ManaShieldDir14 0 9 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 3 3 255 255 255 1 0
chillblood ChillBloodPuff_curse 0 14 all 1 1 0 0 0 0 0 14 0 -14 -60 16 0 3 3 3 81 81 255 1 0
thunderstormfront null 0 2 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 8 8 255 255 255 1 0
thunderstormback ThunderstormCast_operate 0 19 Sorceress 1 1 0 0 0 0 0 0 0 0 0 16 0 3 8 8 255 255 255 1 0
thunderstormcast ThunderstormCast 0 10 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 1 8 255 255 255 1 0
blessedaimfront BlessedAim_front 0 10 Paladin 0 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
blessedaimback BlessedAim_back 0 10 Paladin 1 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
staminafront Stamina_front 0 16 Paladin 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
staminaback Stamina_back 0 16 Paladin 1 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
concentrationfront Concentration_front 0 10 Paladin 0 1 0 0 0 0 0 0 0 0 0 3 0 3 0 0 255 255 255 1 0
concentrationback Concentration_back 0 10 Paladin 1 1 0 0 0 0 0 0 0 0 0 3 0 3 0 0 255 255 255 1 0
holyfreeze HolyFreeze 0 15 Paladin 1 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
cleansingfront Cleansing_front 0 16 Paladin 0 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
cleansingback Cleansing_back 0 16 Paladin 1 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
holyshockfront HolyShock_front 0 14 Paladin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
holyshockback HolyShock_back 0 14 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
holyshockhit ShockHitSm 0 20 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 11 11 255 255 255 1 0
sanctuaryfront SanctuaryAura_Front 0 12 Paladin 1 1 0 0 0 0 -1 0 0 0 0 16 0 3 0 0 255 255 255 1 0
sanctuaryback SanctuaryAura_Back 0 12 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
sanctuaryknockback SanctuaryKnockback 0 13 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
meditationfront Meditation 0 13 Paladin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
meditationback null 0 2 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
fanaticismfront Fanatacism_Front 0 30 Paladin 0 1 0 0 0 0 -1 0 0 0 0 6 0 3 0 0 255 255 255 1 0
fanaticismback Fanatacism_Back 0 30 Paladin 1 1 0 0 0 0 0 0 0 0 0 6 0 3 0 0 255 255 255 1 0
redemptionfront null 0 2 Paladin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
redemptionback Redemption 0 12 Paladin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
convictionfront null 0 2 Paladin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
convictionback Conviction 0 21 all 1 1 0 0 0 0 0 0 0 0 0 6 0 3 0 0 255 255 255 1 0
mephisto MephistoOverlay 0 26 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
conversioncast AuraResistColdBackCast 0 11 Monster 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 1 10 210 210 255 1 0
conversionaura Conversion 0 10 Monster 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
charge Charge 0 8 Paladin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
fingermagespiderexplode FingerMageSpiderXplode 0 6 Monster 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
fingermagecurse FingerMageCurse 0 15 Monster 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
curseamplifydamage CurseAmplifyDamageEffect 0 24 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 1 6 255 64 64 1 0
cursedimvision CurseDimVisionEffect 0 24 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 1 6 255 210 210 1 0
curseweaken CurseWeakenEffect 0 24 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 1 6 255 210 210 1 0
curseironmaiden CurseIronMaidenEffect 0 29 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 1 6 255 0 0 1 0
curseterror CurseTerrorEffect 0 20 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
curseattract CurseAttractHarmEffect 0 24 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 1 6 255 0 0 1 0
cursereversevampire CurseReverseVampireEffect 0 24 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 0 0 0 0 0 1 0
curseconfuse CurseConfuseEffect 0 24 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 1 6 0 255 0 1 0
cursedecrepify CurseDecrepifyEffect 0 24 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 1 6 0 0 255 1 0
curselowerresist CurseLowerResistEffect 0 29 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 1 6 0 0 255 1 0
hit_ironmaiden AuraThornsHit 0 10 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
hit_reversevampire CurseRevVampireHit 0 13 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
bonearmor_cast BoneShieldEmerge 0 12 Necro 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
bonearmor_front BoneShieldFront 0 24 Necro 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
bonearmor_back BoneShieldBack 0 24 Necro 1 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
bonearmor_null1 null 0 2 Necro 0 1 0 1 1 0 0 0 0 0 0 4 0 3 0 0 0 0 0 1 0
bonearmor_null2 null 0 2 Necro 0 1 0 1 1 0 0 0 0 0 0 2 0 3 0 0 0 0 0 1 0
diablolightning RedShockEffectSmall 0 20 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
vulturedemonfeathers vdfeathers 0 26 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 5 2 2 255 255 255 4 0
bash BABash 0 9 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 0 0 0 1 0
stun Stun 0 12 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 0 0 0 1 0
battlecry BattleCry 0 15 all 0 1 0 0 0 0 0 14 0 -14 -60 8 0 3 0 0 0 0 0 1 0
sricehit SRIceHit 0 16 Monster 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 103 193 255 1 0
steallife CurseRevVampireHit 0 13 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
stealmana VampiricHitMana 0 13 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
enchant FireEnchant 0 17 Sorceress 0 1 0 0 0 0 0 0 0 0 0 16 0 3 1 9 255 178 64 1 0
battleorderscast BattleOrdersStart 0 11 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
battleorders BattleOrders 0 20 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
durieldead_rear DurielBloodsplatOverlayRear 0 26 ? 1 1 0 0 0 0 0 0 0 0 0 8 0 5 0 0 255 255 255 1 1
tentacleheadbloodripple BloodRipple 0 16 Monster 0 1 0 0 0 0 0 0 0 0 0 8 0 8 2 2 0 0 0 1 1
handofgod HolyShockHit 0 21 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 2 2 255 255 255 1 0
battlecommandcast BattleCommandStart 0 20 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
battlecommand BattleCommand 0 20 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
berserkfront BerserkOverlay 0 24 Bar 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
berserkback null 0 2 Bar 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
frenzy Frenzy 0 25 Bar 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
firegolem FireGolemOverlay 0 16 all 0 1 0 0 0 0 0 0 0 0 0 10 0 3 2 5 255 0 0 1 0
valkyriestart ValkarieEmerge 0 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
valkyrie ValkarieGlow 0 20 all 1 1 0 0 0 5 0 0 0 0 0 8 0 3 0 0 255 255 255 1 0
whirlwind BAWhirlwind01 0 8 Bar 0 1 0 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 1 0
vampiresteal VampiricHitHealth 0 8 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
fingermageflames FingerMageFlames 0 14 Monster 0 1 0 1 1 0 0 0 0 0 0 16 0 3 2 2 255 64 64 1 0
warcry WarcryHit 0 16 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
dopllezon_appear DopplezonAppear 0 19 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 1 4 255 255 255 1 0
hydra_start1 HydraGroundFireStart 0 16 Monster 0 1 0 0 0 -6 -11 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_start2 HydraGroundFireStart 0 16 Monster 0 1 0 0 0 0 -7 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_start3 HydraGroundFireStart 0 16 Monster 0 1 0 0 0 4 -15 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_loop1 HydraGroundFire 0 20 Monster 0 1 0 0 0 -6 -11 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_loop2 HydraGroundFire 0 20 Monster 0 1 0 0 0 0 -7 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_loop3 HydraGroundFire 0 20 Monster 0 1 0 0 0 4 -15 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_end1 HydraGroundFireEnd 0 16 Monster 0 1 0 0 0 -6 -11 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_end2 HydraGroundFireEnd 0 16 Monster 0 1 0 0 0 0 -7 0 0 0 0 16 0 3 0 0 255 255 255 1 0
hydra_end3 HydraGroundFireEnd 0 16 Monster 0 1 0 0 0 4 -15 0 0 0 0 16 0 3 0 0 255 255 255 1 0
light_jet LIGHTJET 0 13 object 0 1 0 0 0 0 0 0 0 0 0 4 0 3 0 0 255 255 255 1 0
horadric_light HoradricLightBeam 0 21 object 0 1 0 0 0 0 0 0 0 0 0 5 0 3 0 0 255 255 255 1 0
monfrenzy FrenzyThornhulk 0 25 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
willowispdeath willodeathoverlay 0 20 Monster 0 1 0 0 0 0 0 0 0 0 0 20 0 3 1 4 178 178 178 1 0
megademondeath firedeath 0 20 Monster 0 1 0 0 0 0 0 0 0 0 0 8 0 3 2 2 255 64 64 1 0
light_cast_2 LightningCastRunesFront 0 10 Sorceress 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 9 255 255 255 1 0
undeadhorrordeath UMFire 0 20 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 2 2 255 255 255 1 0
fetishdeath1 BoneFetishDeathBones 0 11 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 5 0 0 255 255 255 1 0
fetishdeath2 BoneFetishDeathCloud 0 10 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
Expansion
psychic_hammer_hit Expansion\PsyHammerHit 100 7 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 0 0 255 255 255 1 0
psychic_hammer_curse Expansion\PsyHammerOverlay 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
fist_will_cast Expansion\PsyHammerOverlay 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
frontkick Willodeathoverlay 100 20 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
druid_fire_cast_1 Expansion\Fire_Overlay_A 100 21 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
druid_fire_cast_2 Expansion\Fire_Overlay_B 100 21 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
arctic_blast_cast Expansion\ColdWindOverlay 100 21 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
siege_beast_dust Expansion\SeigeBeast_Dust 100 14 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
wolf_into expansion\druidmorph 100 13 all 0 1 0 0 0 0 0 0 0 0 0 20 0 3 0 0 255 255 255 1 0
wolf_undo expansion\druidmorph 100 13 all 0 1 0 0 0 0 0 0 0 0 0 10 0 3 0 0 255 255 255 1 0
bear_into expansion\druidmorph 100 13 all 0 1 0 0 0 0 0 0 0 0 0 20 0 3 0 0 255 255 255 1 0
bear_undo expansion\druidmorph 100 13 all 0 1 0 0 0 0 0 0 0 0 0 10 0 3 0 0 255 255 255 1 0
spawnedminion Expansion\minionspawnover 100 20 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
bloodlust_state BloodRipple 100 15 all 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
suicide_death Expansion\Suicide_Death_Overlay 100 20 all 0 1 0 0 0 0 0 0 0 0 0 6 0 3 0 0 255 255 255 1 1
oak_sage Expansion\oak_sage_Aura 100 11 all 1 1 0 0 0 0 0 0 0 0 0 6 0 3 0 0 255 255 255 1 0
volcano_flame Expansion\volcano_Fire 100 29 all 0 1 0 0 1 0 0 0 0 0 0 16 0 3 0 0 255 255 255 1 0
cyclonearmor1front Expansion\CycloneArmorAFront 100 11 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cyclonearmor1back Expansion\CycloneArmorABack 100 11 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cyclonearmor2front Expansion\CycloneArmorBFront 100 11 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cyclonearmor2back Expansion\CycloneArmorBBack 100 11 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cyclonearmor3front Expansion\CycloneArmorCFront 100 11 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cyclonearmor3back Expansion\CycloneArmorCBack 100 11 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cloaked Expansion\CloakOfShadows 100 16 all 0 1 0 0 0 0 0 -46 -60 -74 -120 16 0 3 1 2 255 210 210 1 0
assassinfistimpact Expansion\DeadlyFistHit 100 5 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 3 3 255 255 255 1 0
assassinfootimpact Expansion\DeadlyFistHit 100 5 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 3 3 255 255 255 1 0
twisterfront Expansion\TwisterFront 100 40 all 0 1 0 0 1 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
twisterback Expansion\TwisterBack 100 40 all 1 1 0 0 1 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
bladeshield Expansion\blade_shield 100 20 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
rabiesplague Expansion\rabies 100 30 all 0 1 0 0 0 0 0 14 0 -14 -60 16 0 3 0 0 0 0 0 1 0
spawnedbirthflames Expansion\SpawnedBirthFlames 100 8 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 1 3 255 0 0 1 0
spawnedflames Expansion\SpawnedFlames 100 8 all 1 1 0 0 0 0 0 0 0 0 0 8 0 3 1 3 255 0 0 1 0
defense_curse expansion\succcurseoverlay1 100 11 all 0 1 0 0 0 0 0 14 0 -14 -60 8 0 3 1 6 255 193 103 1 0
blood_mana expansion\succcurseoverlay2 100 10 all 0 1 0 1 1 0 0 14 0 -14 -60 16 0 3 0 0 0 0 0 1 0
burning Expansion\On_Fire 100 19 all 0 1 0 1 1 0 0 0 0 0 0 16 0 3 1 6 255 64 64 1 0
catapult_death_s Expansion\catadeathoversouth 100 21 all 0 1 0 0 0 0 0 14 0 -14 -60 8 0 3 1 6 255 64 64 1 0
catapult_death_e Expansion\catadeathovereast 100 21 all 0 1 0 0 0 0 0 14 0 -14 -60 8 0 3 1 6 255 64 64 1 0
ice_cage expansion\icecage_neutral 100 1 Monster 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
ice_cage_melt expansion\icecage_melt 100 30 Monster 0 1 0 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 1 0
succubae_death expansion\succ1deathoverlay 100 18 Monster 0 1 0 0 0 0 0 0 0 0 0 10 0 3 0 0 0 0 0 1 0
progressive_damage_1 expansion\MISSILE_A_A 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_damage_2 expansion\MISSILE_A_B 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_damage_3 expansion\MISSILE_A_C 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_steal_1 expansion\MISSILE_B_A 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_steal_2 expansion\MISSILE_B_B 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_steal_3 expansion\MISSILE_B_C 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_other_1 expansion\MISSILE_C_A 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_other_2 expansion\MISSILE_C_B 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_other_3 expansion\MISSILE_C_C 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_fire_1 expansion\MISSILE_RED_A 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_fire_2 expansion\MISSILE_RED_B 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_fire_3 expansion\MISSILE_RED_C 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_fire_2_attack FireCast_for_Sorceress 100 14 all 0 1 0 0 0 0 0 0 0 0 0 32 0 3 0 0 0 0 0 1 0
progressive_fire_3_attack FireCast2 100 16 all 0 1 0 0 0 0 0 0 0 0 0 32 0 3 0 0 0 0 0 1 0
progressive_cold_1 expansion\MISSILE_BLUE_A 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_cold_2 expansion\MISSILE_BLUE_B 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_cold_3 expansion\MISSILE_BLUE_C 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_cold_2_attack IceCastNew01 100 15 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_cold_3_attack IceCastNew02 100 15 all 0 1 0 0 0 0 0 0 0 0 0 32 0 3 0 0 0 0 0 1 0
progressive_lightning_1 expansion\MISSILE_WHITE_A 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_lightning_2 expansion\MISSILE_WHITE_B 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_lightning_3 expansion\MISSILE_WHITE_C 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_lightning_2_attack expansion\skill_norotate 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
progressive_lightning_3_attack expansion\skill_norotate 100 10 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
spirit_of_barbs expansion\spirit_of_barbs 100 12 all 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
heart_of_wolverine expansion\Heart_of_Wolverine 100 30 all 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
impregnated expansion\painwormcurse 100 10 all 1 1 0 1 1 0 0 0 0 0 0 8 0 3 0 0 0 0 0 1 0
quickness expansion\quickness 100 9 Assasin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
venomclaws expansion\venomclaws 100 10 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
dragonflight expansion\DragonFlight 100 9 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul1 expansion\Maul_A_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul2 expansion\Maul_B_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul3 expansion\Maul_C_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul4 expansion\Maul_D_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul5 expansion\Maul_E_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage1 expansion\feral_rage_A_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage2 expansion\feral_rage_B_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage3 expansion\feral_rage_C_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage4 expansion\feral_rage_D_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage5 expansion\feral_rage_E_Front 100 16 Druid 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
shadowwarriorappear expansion\ShadowAppear 100 16 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
shadowwarriordeath expansion\ShadowAppear 100 16 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
fade expansion\fade 100 20 Assasin 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
direwolfcharged expansion\DireWolfCharged 100 16 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
poisonhit expansion\poisonhit 100 8 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 1 0
baal_on_throne expansion\baalshield 100 11 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 1 0
death_sentry expansion\dethsentry01 100 12 all 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul1back expansion\Maul_A_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul2back expansion\Maul_B_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul3back expansion\Maul_C_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul4back expansion\Maul_D_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
maul5back expansion\Maul_E_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage1back expansion\feral_rage_A_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage2back expansion\feral_rage_B_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage3back expansion\feral_rage_C_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage4back expansion\feral_rage_D_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
feralrage5back expansion\feral_rage_E_Back 100 16 Druid 1 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
tigerstrike1 expansion\tiger_Strike_A 100 32 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 1 0
tigerstrike2 expansion\tiger_Strike_B 100 32 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 1 0
tigerstrike3 expansion\tiger_Strike_C 100 32 all 0 1 0 0 0 0 0 0 0 0 0 8 0 3 0 0 0 0 0 1 0
cobrastrike1 expansion\Cobra_Strike_A 100 16 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cobrastrike2 expansion\Cobra_Strike_B 100 16 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0
cobrastrike3 expansion\Cobra_Strike_C 100 16 Assasin 0 1 0 0 0 0 0 0 0 0 0 16 0 3 0 0 0 0 0 1 0

View File

@ -0,0 +1,358 @@
skill Id charclass skilldesc srvstfunc srvdofunc prgstack srvprgfunc1 srvprgfunc2 srvprgfunc3 prgcalc1 prgcalc2 prgcalc3 prgdam srvmissile decquant lob srvmissilea srvmissileb srvmissilec srvoverlay aurafilter aurastate auratargetstate auralencalc aurarangecalc aurastat1 aurastatcalc1 aurastat2 aurastatcalc2 aurastat3 aurastatcalc3 aurastat4 aurastatcalc4 aurastat5 aurastatcalc5 aurastat6 aurastatcalc6 auraevent1 auraeventfunc1 auraevent2 auraeventfunc2 auraevent3 auraeventfunc3 auratgtevent auratgteventfunc passivestate passiveitype passivestat1 passivecalc1 passivestat2 passivecalc2 passivestat3 passivecalc3 passivestat4 passivecalc4 passivestat5 passivecalc5 passiveevent passiveeventfunc summon pettype petmax summode sumskill1 sumsk1calc sumskill2 sumsk2calc sumskill3 sumsk3calc sumskill4 sumsk4calc sumskill5 sumsk5calc sumumod sumoverlay stsuccessonly stsound stsoundclass stsounddelay weaponsnd dosound dosound a dosound b tgtoverlay tgtsound prgoverlay prgsound castoverlay cltoverlaya cltoverlayb cltstfunc cltdofunc cltprgfunc1 cltprgfunc2 cltprgfunc3 cltmissile cltmissilea cltmissileb cltmissilec cltmissiled cltcalc1 *cltcalc1 desc cltcalc2 *cltcalc2 desc cltcalc3 *cltcalc3 desc warp immediate enhanceable attackrank noammo range weapsel itypea1 itypea2 itypea3 etypea1 etypea2 itypeb1 itypeb2 itypeb3 etypeb1 etypeb2 anim seqtrans monanim seqnum seqinput durability UseAttackRate LineOfSight TargetableOnly SearchEnemyXY SearchEnemyNear SearchOpenXY SelectProc TargetCorpse TargetPet TargetAlly TargetItem AttackNoMana TgtPlaceCheck ItemEffect ItemCltEffect ItemTgtDo ItemTarget ItemCheckStart ItemCltCheckStart ItemCastSound ItemCastOverlay skpoints reqlevel maxlvl reqstr reqdex reqint reqvit reqskill1 reqskill2 reqskill3 restrict State1 State2 State3 delay leftskill repeat checkfunc nocostinstate usemanaondo startmana minmana manashift mana lvlmana interrupt InTown aura periodic perdelay finishing passive progressive general scroll calc1 *calc1 desc calc2 *calc2 desc calc3 *calc3 desc calc4 *calc4 desc Param1 *Param1 Description Param2 *Param2 Description Param3 *Param3 Description Param4 *Param4 Description Param5 *Param5 Description Param6 *Param6 Description Param7 *Param7 Description Param8 *Param8 Description InGame ToHit LevToHit ToHitCalc ResultFlags HitFlags HitClass Kick HitShift SrcDam MinDam MinLevDam1 MinLevDam2 MinLevDam3 MinLevDam4 MinLevDam5 MaxDam MaxLevDam1 MaxLevDam2 MaxLevDam3 MaxLevDam4 MaxLevDam5 DmgSymPerCalc EType EMin EMinLev1 EMinLev2 EMinLev3 EMinLev4 EMinLev5 EMax EMaxLev1 EMaxLev2 EMaxLev3 EMaxLev4 EMaxLev5 EDmgSymPerCalc ELen ELevLen1 ELevLen2 ELevLen3 ELenSymPerCalc aitype aibonus cost mult cost add
Attack 0 attack 1 1 1 1 0 both weap tpot A1 A1 A1 1 1 1 1 1 1 1 1 0 8 0 0 1 1 1 1 8 128 0
Kick 1 kick 2 2 0 0 0 h2h 4 KK KK xx 1 1 1 1 0 8 0 0 1 1 1 8 0
Throw 2 throw 65 3 2 2 0 rng thro TH TH xx 1 1 1 2 0 8 0 0 1 1 1 8 128 0
Unsummon 3 unsummon 3 4 3 0 0 rng SC SC xx 1 1 1 1 0 8 0 0 1 1 1 1 8 0
Left Hand Throw 4 left hand throw 65 5 2 2 0 rng 1 thro S4 S4 xx 1 1 1 1 3 0 8 0 0 1 1 1 8 0
Left Hand Swing 5 left hand swing 1 1 4 1 0 both 1 weap tpot S3 S3 S3 1 1 1 1 1 1 4 0 8 0 0 1 1 1 8 0
Magic Arrow 6 ama magic arrow magicarrow magicarrow 1 0 1 rng miss A1 A1 xx 1 1 20 1 0 5 12 -1 1 1 10 9 8 128 1 1 1 1 1 1 1 1 1 1 1 1 256 1000
Fire Arrow 7 ama fire arrow 4 firearrow 1 11 firearrow 1 0 rng miss A1 A1 xx 1 1 20 1 1 1 5 24 1 1 12 damage synergy 1 10 9 8 128 fire 1 2 3 6 12 24 4 2 3 7 14 27 (skill('Exploding Arrow'.blvl)) * par8 256 1000
Inner Sight 8 ama inner sight 6 34179 innersight ln34 ln56 armorclass -edmn amazon_eyeofzeus cast_innersight 1 3 none SC SC xx 4 1 amazon_eyeofzeus cast_innersight 1 20 1 7 10 0 1 40 base monster ac 25 monster ac/lvl 200 duration 100 duration per level 20 radius of effect 0 radius per level 1 8 40 25 45 60 80 100 256 1000
Critical Strike 9 ama critical strike criticalstrike passive_critical_strike dm12 1 4 rng spea miss xx 1 1 20 1 0 8 0 0 1 1 5 min % to do double damage 80 max % to do double damage 1 8 256 1000
Jab 10 ama jab 5 7 amazon_jab_1 weapon_1ht_1 weapon_2ht_1 12 16 1 3 h2h spea SQ A1 xx 1 1 1 1 1 1 1 20 1 1 6 8 1 ln34 dmg% -15 Percent damage base 3 Percent damage per level 1 10 9 8 128 256 1000
Cold Arrow 11 ama cold arrow 4 coldarrow 1 11 coldarrow 1 4 rng miss A1 A1 xx 1 6 20 1 1 1 5 28 1 1 12 damage synergy 1 10 9 7 128 cold 6 4 5 8 16 42 8 4 5 9 17 44 (skill('Ice Arrow'.blvl)) * par8 100 30 30 30 384 3000
Multiple Shot 12 ama multiple shot 4 8 1 multipleshotarrow multipleshotbolt 11 17 multipleshotarrow multipleshotbolt 1 7 rng miss A1 A1 xx 1 6 20 Magic Arrow 1 1 1 8 4 1 1 "min(24,ln12)" # missiles par3 activation frame 2 triggered 2 number of arrows to fire 1 additional arrows per level 1 Acivation frame of arrows. 1 8 96 384 3000
Dodge 13 ama dodge dodge passive_dodge dm12 amazon_dodge_1 1 0 h2h S1 S1 xx 6 20 1 0 8 0 0 1 1 10 min % dodge 65 max % dodge 1 8 384 3000
Power Strike 14 ama power strike 6 2 1 4 h2h spea A1 A1 xx 1 1 1 1 6 20 Jab 1 1 6 8 1 1 10 damage synergy 1 20 12 8 128 ltng 1 0 0 0 0 0 16 18 36 54 72 90 (skill('Lightning Strike'.blvl)+skill('Lightning Bolt'.blvl)+skill('Charged Strike'.blvl)+skill('Lightning Fury'.blvl)) * par8 384 3000
Poison Javelin 15 ama poison javelin 4 poisonjav 1 amazon_cast_poison 1 11 poisonjav 1 2 rng jave TH TH xx 1 6 20 15 1 1 6 16 1 1 12 damage synergy 1 128 pois 32 16 32 48 64 96 48 16 36 52 68 84 (skill('Plague Javelin'.blvl)) * par8 200 50 50 50 384 3000
Exploding Arrow 16 ama exploding arrow 4 explodingarrow 1 11 explodingarrow 1 5 rng miss A1 A1 xx 1 12 20 Fire Arrow Multiple Shot 1 1 1 7 10 1 1 12 damage synergy 1 20 9 8 128 fire 2 5 7 9 12 20 6 5 8 11 14 23 (skill('Fire Arrow'.blvl)) * par8 512 8000
Slow Missiles 17 ama slow missiles 6 50563 slowmissiles ln34 ln56 skill_handofathena ln12 amazon_handofathena cast_slowmissiles 1 8 none SC SC xx 4 1 amazon_handofathena cast_slowmissiles 12 20 Inner Sight 1 7 10 0 1 33 % velocity for missiles 0 % velocity per level 300 duration 150 duration per level 20 radius of effect 0 radius per level 1 8 512 8000
Avoid 18 ama avoid avoid passive_avoid dm12 amazon_dodge_1 1 0 h2h S1 S1 xx 12 20 Dodge 1 0 8 0 0 1 1 15 min % avoid 75 max % avoid 1 8 512 8000
Impale 19 ama impale 7 2 amazon_impale_1 1 1 1 6 h2h spea SQ A1 xx 8 1 1 1 1 1 12 20 Jab 1 1 8 3 0 1 ln12 dm% par6-dm34 dur loss chance par5 dur loss 300 Percent damage 25 % damage per level 0 % chance durability loss delta min 30 % chance durability loss delta max 1 durability loss 50 % max chance durability loss 1 100 25 8 128 512 8000
Lightning Bolt 20 ama lightning bolt 4 lightningjavelin 1 11 lightningjavelin 1 6 rng jave TH TH xx 12 20 Poison Javelin 1 1 6 24 1 1 3 damage synergy 1 8 96 ltng 1 0 0 0 0 0 40 12 18 28 48 88 (skill('Lightning Strike'.blvl)+skill('Power Strike'.blvl)+skill('Charged Strike'.blvl)+skill('Lightning Fury'.blvl)) * par8 512 8000
Ice Arrow 21 ama ice arrow 4 icearrow 1 11 icearrow 1 6 rng miss A1 A1 xx 1 18 20 Cold Arrow 1 1 1 6 16 1 1 5 length synergy 8 damage synergy 1 20 9 8 128 cold 6 6 12 18 26 36 10 6 13 19 27 38 (skill('Cold Arrow'.blvl))*par8 50 5 5 5 (skill('Freezing Arrow'.blvl)) * par7 640 16000
Guided Arrow 22 ama guided arrow 4 10 1 guidedarrow guidedarrow guidedarrow 11 18 guidedarrow guidedarrow guidedarrow 1 4 rng miss A1 A1 xx 1 18 20 Cold Arrow Multiple Shot 1 1 1 6 32 -1 1 ln34 + dmg% 0 Damage percent 5 damage % per level 1 8 128 640 16000
Penetrate 23 ama penetrate penetrate item_tohit_percent ln12 1 1 rng spea miss xx 18 20 Critical Strike 1 0 8 0 0 1 1 35 add % to hit with missiles base 10 add % to hit with missiles/level 1 8 640 16000
Charged Strike 24 ama charged strike 6 11 chargedstrikebolt chargedstrikebolt chargedstrikebolt 19 chargedstrikebolt chargedstrikebolt chargedstrikebolt 1 5 h2h spea A1 A1 xx 1 1 1 1 18 20 Power Strike Lightning Bolt 1 1 6 16 1 1 par1+lvl/par2 # bolts 3 Bolts to send out 5 Additional bolts per N levels 10 damage synergy 1 8 ltng 1 0 0 0 0 0 30 12 16 20 24 28 (skill('Lightning Strike'.blvl)+skill('Lightning Bolt'.blvl)+skill('Power Strike'.blvl)+skill('Lightning Fury'.blvl)) * par8 640 16000
Plague Javelin 25 ama plague javelin 4 plaguejavelin 1 amazon_cast_poison 1 11 plaguejavelin 1 3 rng jave TH TH xx 1 18 20 Lightning Bolt 100 1 1 1 7 14 1 1 10 damage synergy 1 30 9 3 128 pois 10 6 12 20 40 60 16 6 12 20 40 60 (skill('Poison Javelin'.blvl))*par8 75 10 10 10 640 16000
Strafe 26 ama strafe 8 12 strafearrow strafebolt par5 amazon_dodge_1 paladin_charge 13 20 multipleshotarrow multipleshotbolt 1 0 rng miss A1 A1 xx 1 24 20 Guided Arrow 1 1 1 8 11 0 "min(par3 + lvl - 1, par4)" #missiles ln12 damage % 2+lvl/4 min # missiles 5 Damage increase 5 Damage increase per level 4 Base Shots to take (+1 per level) 10 Max shots to take 35 Radius 50 % frame rollback 1 8 96 768 32000
Immolation Arrow 27 ama immolation arrow 4 immolationarrow 1 11 immolationarrow 1 6 rng miss A1 A1 xx 1 24 20 Exploding Arrow 25 1 1 1 7 12 1 1 par1 fire disc radius par2 damage radius 3 Fire disc radius 4 Radial Damage Radius 10 damage synergy 1 30 9 8 128 fire 12 12 23 34 36 38 23 12 23 34 36 38 (skill('Exploding Arrow'.blvl)) * par8 768 32000
Dopplezon 28 ama dopplezon 15 dopplezon fireresist "min(lvl*par7,85)" lightresist "min(lvl*par7,85)" coldresist "min(lvl*par7,85)" poisonresist "min(lvl*par7,85)" dopplezon dopplezon 1 NU dopllezon_appear amazon_valkyrie_cast 1 4 none SC SC xx 4 24 20 Slow Missiles 1 6 76 -3 1 lvl*par4 bonus hp% ln12 duration par3 %hp of caster 250 duration of decoy 125 duration per level 50 percent hitpoints of caster 10 hp % bonus per level 4 resistances 1 8 768 32000
Evade 29 ama evade evade passive_evade dm12 amazon_dodge_1 1 0 h2h S1 S1 xx 24 20 Avoid 1 0 8 0 0 1 1 10 min % evade 65 max % evade 1 8 768 32000
Fend 30 ama fend 9 13 amazon_jab_1 1 14 21 1 0 h2h spea A1 A1 xx 9 1 1 1 1 24 20 Impale 1 1 8 5 0 12 max targets ln34 damage % 1 additional target/level 60 % frame rollback 70 % damage percent 10 damage % per level 1 40 10 8 128 768 32000
Freezing Arrow 31 ama freezing arrow 4 freezingarrow 1 11 freezingarrow 1 7 rng miss A1 A1 xx 1 30 20 Ice Arrow 1 1 1 7 18 1 1 par1 damage radius 5 Radius of impact 5 length synergy 12 damage synergy 1 40 9 8 128 cold 40 10 15 20 22 24 50 10 15 20 22 24 (skill('Cold Arrow'.blvl))*par8 50 (skill('Ice Arrow'.blvl)) * par7 896 64000
Valkyrie 32 ama valkyrie 16 strength lvl * par2 dexterity lvl * par4 fireresist "min((lvl+skill('Dopplezon'.blvl))*par7,85)" lightresist "min((lvl+skill('Dopplezon'.blvl))*par7,85)" coldresist "min((lvl+skill('Dopplezon'.blvl))*par7,85)" poisonresist "min((lvl+skill('Dopplezon'.blvl))*par7,85)" item_armor_percent par3 * (lvl - 1) tohit toht valkyrie valkyrie 1 NU dodge skill('Dodge'.blvl) avoid skill('Avoid'.blvl) Evade skill('Evade'.blvl) Critical Strike skill('Critical Strike'.blvl) amazon_valkyrie_cast 1 6 none SC SC xx 4 1 30 20 Dopplezon Evade 150 1 8 25 1 1 1 par1 * (lvl - 1) + skill('Dopplezon'.blvl) * par8 bonus hp% ln56 itemlevel 20 % hitpoints more per level 25 str per level 10 % AC increase per level 12 dex per level 25 Magic Item level 3 Magic Item level per level 2 resistances 20 hp synergy 1 40*lvl+40*skill('Penetrate'.blvl) 8 896 64000
Pierce 33 ama pierce pierce skill_pierce dm12 1 0 rng miss xx 30 20 Penetrate 1 0 8 0 0 1 1 1 10 min % chance 100 max % chance 1 8 896 64000
Lightning Strike 34 ama lightning strike 10 14 lightningstrike lightningstrike lightningstrike par1 22 lightningstrike lightningstrike lightningstrike 1 8 h2h spea A1 A1 xx 1 1 1 1 30 20 Charged Strike 1 1 8 9 0 1 20 radius ln34 hits 20 radius of jump to next target 2 Target hits 1 Target hits per level 8 damage synergy 1 8 ltng 1 0 0 0 0 0 25 10 15 20 25 30 (skill('Charged Strike'.blvl)+skill('Lightning Bolt'.blvl)+skill('Power Strike'.blvl)+skill('Lightning Fury'.blvl)) * par8 896 64000
Lightning Fury 35 ama lightning fury 4 lightningfury 1 42371 par3 11 lightningfury 1 8 rng jave TH TH xx 30 20 Plague Javelin 1 1 7 20 1 1 ln12 num targets 2 lightning spells from target 1 extra spells/level 15 Target search radius 1 damage synergy 1 8 128 ltng 1 0 0 0 0 0 40 20 30 40 50 50 (skill('Charged Strike'.blvl)+skill('Lightning Bolt'.blvl)+skill('Power Strike'.blvl)+skill('Lightning Strike'.blvl)) * par8 896 64000
Fire Bolt 36 sor fire bolt firebolt sorceress_cast_fire fire_cast_1 firebolt 1 2 none SC SC xx 1 sorceress_cast_fire 1 20 1 1 7 5 0 1 16 damage synergy 1 7 fire 6 3 4 8 18 54 12 3 6 10 20 56 (skill('Fire Ball'.blvl)+skill('Meteor'.blvl))*par8 256 1000
Warmth 37 sor warmth warmth manarecoverybonus ln12 1 0 none SC SC xx 1 20 1 0 8 0 0 1 1 1 30 increase in mana recovery 12 additional increase/level 1 8 fire 256 1000
Charged Bolt 38 sor charged bolt 17 chargedbolt chargedbolt chargedbolt sorceress_cast_lightning light_cast_1 23 chargedbolt 1 1 none SC SC xx 1 sorceress_cast_lightning 1 20 1 1 5 24 4 1 "min(24,ln12)" # bolts 3 Bolts to send out 1 Additional bolts per level 6 damage synergy 1 7 ltng 4 1 1 2 3 4 8 1 1 2 3 4 (skill('Lightning'.blvl))*par8 256 1000
Ice Bolt 39 sor ice bolt icebolt sorceress_cast_cold ice_cast_1 icebolt 1 5 none SC SC xx 1 sorceress_cast_cold 1 20 1 1 8 3 0 1 15 damage synergy 1 7 cold 6 2 4 6 8 10 10 3 5 7 9 11 (skill('Frost Nova'.blvl)+skill('Ice Blast'.blvl)+skill('Glacial Spike'.blvl)+skill('Blizzard'.blvl)+skill('Frozen Orb'.blvl))*par8 150 35 35 35 256 1000
Frozen Armor 40 sor frozen armor 18 frozenarmor ln34+(skill('Shiver Armor'.blvl)+skill('Chilling Armor'.blvl))*par7 skill_armor_percent ln12 damagedinmelee 2 sorceress_cast_cold sorceress_frozenarmor ice_cast_1 frozenarmor_hit 1 0 none SC SC xx 1 sorceress_cast_cold ice_cast_1 1 20 1 8 7 0 1 1 ln56*(100+((skill('Shiver Armor'.blvl)+skill('Chilling Armor'.blvl))*par8))/100 freeze length 30 % AC base 5 % AC per level 3000 Duration 300 Duration per level 30 Freeze Frames 3 Freeze Frames per level 250 duration synergy 5 freeze synergy 1 8 1 256 1000
Inferno 41 sor inferno 11 19 infernoflame1 infernoflame1 infernoflame1 ln12/4-2 15 24 infernoflame1 infernoflame2 1 7 rng SQ SQ xx 6 10 6 20 1 1 6 0 2 36 1 1 ln12/2 range 20 frames if monster 20 base ranged (doubled) 3 level range (doubled) 6 Min Mana to start casting 13 damage synergy 1 2 fire 32 24 26 28 32 36 64 24 27 29 33 37 (skill('Warmth'.blvl))*par8 384 3000
Static Field 42 sor static field 20 34691 ln12 sorceress_cast_lightning light_cast_1 1 10 none SC SC xx 4 1 sorceress_cast_lightning 6 20 1 8 9 0 1 par4 damage % par3 min damage 5 Base radius of effect. 1 Level radius bonus. 0 Minimum Damage. 25 Percent Damage. 1 8 ltng 384 3000
Telekinesis 43 sor telekinesis 12 21 par1 sorceress_cast_lightning sorceress_telekinesis light_cast_2 1 10 none SC SC xx 1 sorceress_telekinesis 6 20 1 8 7 0 1 1 25 On-screen cast range. 35 Chance to knockback/stun. 1 5 2 109 8 ltng 1 1 1 1 1 1 2 1 1 1 1 1 384 3000
Frost Nova 44 sor frost nova 22 frostnova frostnova frostnova sorceress_cast_cold ice_cast_2 25 frostnova 1 6 none SC SC SC 36 10 sorceress_cast_cold 6 20 1 8 9 1 1 9 radius of freeze 3 additional radius/level 10 damage synergy 1 7 cold 4 4 6 8 10 12 8 5 7 9 11 13 (skill('Blizzard'.blvl)+skill('Frozen Orb'.blvl))*par8 200 25 25 25 384 3000
Ice Blast 45 sor ice blast iceblast sorceress_cast_cold ice_cast_1 iceblast 1 6 none SC SC xx 1 sorceress_cast_cold 6 20 Ice Bolt 1 1 7 12 1 1 75 Duration of freeze 5 additional duration/level 10 freeze synergy 8 damage synergy 1 7 cold 16 14 28 42 56 70 24 15 29 43 57 71 (skill('Ice Bolt'.blvl)+skill('Blizzard'.blvl)+skill('Frozen Orb'.blvl))*par8 75 5 5 5 (skill('Glacial Spike'.blvl))*par7 384 3000
Blaze 46 sor blaze 23 blaze blaze blaze blaze dm12 sorceress_cast_fire fire_cast_2 blaze firewall firesmall firemedium 1 3 none SC SC xx 1 12 20 Inferno 1 7 22 1 1 50 Min Frames 500 Max Frames 1 damage synergy 4 damage synergy 1 4 fire 4 2 3 4 6 9 8 2 3 4 6 9 skill('Warmth'.blvl)*par8+skill('Fire Wall'.blvl)*par7 512 8000
Fire Ball 47 sor fire ball fireball sorceress_cast_fire fire_cast_2 fireball 1 5 none SC SC xx 1 sorceress_cast_fire 12 20 Fire Bolt 1 1 7 10 1 1 14 damage synergy 1 7 fire 12 13 23 28 33 38 28 15 25 30 35 40 (skill('Fire Bolt'.blvl)+skill('Meteor'.blvl))*par8 512 8000
Nova 48 sor nova 22 nova nova nova sorceress_cast_lightning light_cast_1 25 nova 1 2 none SC SC xx 36 10 sorceress_cast_lightning 12 20 Static Field 1 8 15 1 1 12 number of missiles 4 additional missiles per level 1 8 ltng 1 6 7 8 9 10 20 8 9 10 11 12 512 8000
Lightning 49 sor lightning lightningbolt sorceress_cast_lightning light_cast_1 lightningbolt 1 6 none SQ SC xx 12 1 1 sorceress_cast_lightning 12 20 Charged Bolt 1 1 7 16 1 1 10 minimum damage 20 max damage 4 increase in dam/level (min & max) 8 damage synergy 1 8 ltng 1 0 0 0 0 0 40 8 12 20 28 36 (skill('Charged Bolt'.blvl)+skill('Chain Lightning'.blvl)+skill('Nova'.blvl))*par8 512 8000
Shiver Armor 50 sor shiver armor 18 shiverarmor ln34+(skill('Frozen Armor'.blvl)+skill('Chilling Armor'.blvl))*par7 skill_armor_percent ln12 attackedinmelee 3 sorceress_cast_cold sorceress_shiverarmor ice_cast_2 shiverarmor_hit sparkle 10 sparkle delay 5 sparkle radius 3 sparkle height 1 0 none SC SC xx sorceress_cast_cold ice_cast_3 12 20 Ice Blast Frozen Armor 1 8 11 0 1 1 45 % AC base 6 % AC per level 3000 Duration 300 duration per level 250 duration synergy 9 damage synergy 1 7 cold 12 4 6 8 10 12 16 5 7 9 11 13 (skill('Frozen Armor'.blvl)+skill('Chilling Armor'.blvl))*par8 100 25 50 512 8000
Fire Wall 51 sor fire wall 24 firewallmaker firewall sorceress_cast_fire fire_cast_2 26 firewallmaker 1 4 none SC SC SC 4 1 1 sorceress_cast_fire 18 20 Blaze 35 1 8 22 1 1 1 damage synergy 4 damage synergy 1 4 fire 15 9 14 21 21 21 20 9 14 21 21 21 (skill('Warmth'.blvl)*par8+skill('Inferno'.blvl)*par7 640 16000
Enchant 52 sor enchant 25 enchant ln12 firemindam enma firemaxdam exma item_tohit_percent toht sorceress_enchant 1 4 none SC SC xx 1 1 1 1 sorceress_enchant 18 20 Warmth Fire Ball 1 8 25 1 1 1 3600 duration 600 duration per level 33 missile pct 9 damage synergy 1 20 9 7 fire 16 3 7 11 15 19 20 5 9 13 17 21 (skill('Warmth'.blvl))*par8 640 16000
Chain Lightning 53 sor chain lightning 26 chainlightning chainlightning chainlightning par1 sorceress_cast_lightning light_cast_1 27 chainlightning chainlightning chainlightning 1 2 none SQ SC SC 12 1 151 96 sorceress_cast_lightning 18 20 Lightning 1 1 8 9 1 1 ln34 / 5 # hits 20 radius of jump to next target 26 bolts (5ths) 1 bolts per level (5ths) 5 5ths 4 damage synergy 1 8 ltng 1 0 0 0 0 0 40 11 13 15 15 15 (skill('Charged Bolt'.blvl)+skill('Lightning'.blvl)+skill('Nova'.blvl))*par8 640 16000
Teleport 54 sor teleport 27 sorceress_teleport teleport 1 1 0 none SC SC xx 1 1 2 sorceress_teleport teleport 18 20 Telekinesis 1 8 24 -1 1 1 8 640 16000
Glacial Spike 55 sor glacial spike glacialspike ln34 * (100 + skill('Blizzard'.blvl) * par7) / 100 ln12 sorceress_cast_cold ice_cast_2 glacialspike 1 7 none SC SC SC 1 sorceress_cast_cold 18 20 Ice Blast 1 1 7 20 1 1 4 radius 0 radius per level 50 freeze frames 3 freeze frames per level 3 freeze synergy 5 damage synergy 1 7 cold 32 14 26 28 30 32 48 15 27 29 31 33 (skill('Ice Bolt'.blvl)+skill('Ice Blast'.blvl)+skill('Frozen Orb'.blvl))*par8 640 16000
Meteor 56 sor meteor 28 meteorcenter meteorcenter meteorcenter ln12 sorceress_cast_fire sorceress_meteor fire_cast_2 28 meteorcenter 1 6 none SC SC xx 4 1 1 sorceress_cast_fire fire_cast_2 24 20 Fire Ball Fire Wall 30 1 7 34 1 1 ln12 radius 6 radius of explosion 0 radius per level 30 Frames of fire 15 Frames of fire per level 5 damage synergy 1 8 fire 80 23 39 79 81 83 100 25 41 81 83 85 (skill('Fire Bolt'.blvl)+skill('Fire Ball'.blvl))*par8 768 32000
Thunder Storm 57 sor thunder storm 13 29 thunderstorm1 thunderstorm ln12 sorceress_thunder_cast lightning light_cast_2 84 thunderstorm1 1 5 none SC SC xx 24 20 Nova Chain Lightning 1 8 19 0 1 1 (100-dm56) * par4/100 + par3 800 duration 200 additional duration/level 25 minimum repeat time 100 repeat time factor 0 min % repeat time 100 max % repeat time 17 radius 1 8 ltng 1 10 10 11 11 11 100 10 10 11 11 11 768 32000
Energy Shield 58 sor energy shield 23 energyshield ln12 absorbdamage 24 sorceress_cast_lightning sorceress_energyshield energyshieldhit0 light_cast_2 1 4 none SC SC xx 24 20 Teleport Chain Lightning 1 8 5 0 1 1 "min(edmn,95)" damage absorption par5-skill('Telekinesis'.blvl) ratio 3600 duration 1500 additional duration/level 32 Mana Damage mult in sixteenths 1 8 20 5 2 1 1 1 768 32000
Blizzard 59 sor blizzard 28 blizzardcenter blizzardcenter blizzardcenter sorceress_cast_cold ice_cast_3 28 blizzardcenter 1 7 none SC SC xx 4 1 1 sorceress_cast_cold ice_cast_3 24 20 Frost Nova Glacial Spike 45 1 8 23 1 1 par1 radius par3 frequency 7 radius 4 Missile delay 0 Change duration in Missiles.xls 5 damage synergy 1 8 cold 45 15 30 45 55 65 75 16 31 46 56 66 (skill('Ice Bolt'.blvl)+skill('Ice Blast'.blvl)+skill('Glacial Spike'.blvl))*par8 100 768 32000
Chilling Armor 60 sor chilling armor 18 chillingarmorbolt chillingarmor ln34+(skill('Frozen Armor'.blvl)+skill('Shiver Armor'.blvl))*par7 skill_armor_percent ln12 hitbymissile 1 sorceress_cast_cold sorceress_shiverarmor ice_cast_3 chillarmor_hit chillingarmorbolt 1 5 none SC SC xx 1 sorceress_cast_cold 24 20 Shiver Armor 1 8 17 0 1 1 45 % AC Bonus 5 % AC bonus per level 3600 duration 150 duration per level 250 duration synergy 7 damage synergy 1 7 cold 8 2 4 6 8 10 12 3 5 7 9 11 (skill('Frozen Armor'.blvl)+skill('Shiver Armor'.blvl))*par8 100 25 25 768 32000
Fire Mastery 61 sor fire mastery firemastery passive_fire_mastery ln12 1 3 none SC SC xx 30 20 1 0 8 0 0 1 1 30 % Damage bonus 7 % Damage bonus per level 1 8 fire 896 64000
Hydra 62 sor hydra 14 144 passive_fire_mastery stat('passive_fire_mastery'.accr) passive_fire_pierce stat('passive_fire_pierce'.accr) hydra1 hydra 99 S2 HydraMissile lvl Fire Bolt skill('Fire Bolt'.blvl) Fire Ball skill('Fire Ball'.blvl) 1 sorceress_cast_fire fire_cast_2 17 1 7 none SC SC S1 4 1 1 sorceress_cast_fire fire_cast_2 30 20 Enchant 40 1 7 40 1 1 250 duration 0 additional duration/level 3 damage synergy 1 7 fire 28 11 15 19 23 27 39 13 17 21 25 29 (skill('Fire Bolt'.blvl) + skill('Fire Ball'.blvl))*par8 896 64000
Lightning Mastery 63 sor lightning mastery lightningmastery passive_ltng_mastery ln12 1 5 none SC SC xx 30 20 1 0 8 0 0 1 1 50 % Damage bonus 12 % Damage bonus per level 1 8 896 64000
Frozen Orb 64 sor frozen orb frozenorb sorceress_cast_cold ice_cast_3 29 frozenorb 1 8 none SC SC xx 1 sorceress_cast_cold 30 20 Blizzard 25 1 1 7 50 1 1 2 damage synergy 1 7 cold 80 20 24 28 29 30 90 21 25 29 30 31 (skill('Ice Bolt'.blvl))*par8 200 25 25 25 896 64000
Cold Mastery 65 sor cold mastery coldmastery passive_cold_pierce ln12 1 3 none SC SC xx 30 20 1 0 8 0 0 1 1 20 lower resist 5 lower resist per level 1 8 896 64000
Amplify Damage 66 nec amplify damage 30 3 amplifydamage ln34 ln12 damageresist -par5 necromancer_curse_cast 18 30 curseamplifydamage cursecast 1 10 none SC SC S2 4 1 necromancer_curse_cast 1 20 1 8 4 0 1 3 radius 1 radius per level 200 duration 75 additional duration/level 100 % additional damage taken 1 8 256 1000
Teeth 67 nec teeth 8 teeth necromancer_bone_cast 19 17 teeth teeth bonecast 1 1 none SC SC xx 1 necromancer_bone_cast 1 20 1 1 7 6 1 1 "min(ln12,24)" # missiles par3 activation frame 2 number of missiles 1 additional missiles/level 0 Acivation frame of teeth 15 damage synergy 1 7 mag 4 2 2 3 4 5 8 2 3 4 5 6 (skill('Bone Wall'.blvl)+skill('Bone Prison'.blvl)+skill('Bone Spear'.blvl)+skill('Bone Spirit'.blvl))*par8 256 1000
Bone Armor 68 nec bone armor 18 bonearmor bonearmor (ln12 + (skill('Bone Wall'.blvl) + skill('Bone Prison'.blvl)) * par8)*256 bonearmormax (ln12 + (skill('Bone Wall'.blvl) + skill('Bone Prison'.blvl)) * par8)*256 absorbdamage 22 necromancer_bonearmor 1 3 none SC SC xx 1 necromancer_bonearmor 1 20 1 8 11 1 1 1 20 damage absorbed 10 additional absorbed/level 15 absorb synergy 1 8 256 1000
Skeleton Mastery 69 nec skeleton mastery skel_mastery 1 0 none SC SC xx 1 20 Raise Skeleton 0 8 0 0 1 1 8 additional hit points/level 2 additional damage per level 5 hp% per level for revive 10 dmg% per level for revive 1 8 256 1000
Raise Skeleton 70 nec raise skeleton 15 31 damagepercent ((lvl < 4) ? 0 : ((lvl-3)*par3)) tohit (lvl+skill('Skeleton Mastery'.lvl))*par4 armorclass (lvl+skill('Skeleton Mastery'.lvl))*par5 maxhp skill('Skeleton Mastery'.lvl) * skill('Skeleton Mastery'.par1) * 256 item_normaldamage skill('Skeleton Mastery'.lvl) * skill('Skeleton Mastery'.par2) + edmn necroskeleton skeleton (lvl < 4) ?lvl:(2+lvl/3) S1 1 necromancer_golem_cast 20 31 corpseexplosion 1 0 none SC SC xx 1 2 1 1 20 1 8 6 1 1 (lvl < 4) ? 0 : (par2 * (lvl - 3)) hp % adjustment 5 % chance of shield 50 hp%/lvl 7 dmg%/lvl 15 to hit/lvl 15 ac/lvl 1 8 0 0 1 2 3 4 256 1000
Dim Vision 71 nec dim vision 30 2 dimvision ln34 ln12 necromancer_curse_cast 18 30 cursedimvision cursecast 1 0 none SC SC xx 4 1 necromancer_curse_cast 6 20 1 8 9 0 1 4 radius 1 radius per level 175 duration 50 additional duration/level 1 8 384 3000
Weaken 72 nec weaken 30 3 weaken ln34 ln12 damagepercent -par5 necromancer_curse_cast 18 30 curseweaken cursecast 1 9 none SC SC SC 4 1 necromancer_curse_cast 6 20 Amplify Damage 1 8 4 0 1 9 radius 1 radius per level 350 duration 60 additional duration/level 33 % damage target can do 1 8 384 3000
Poison Dagger 73 nec poison dagger 16 32 1 4 h2h knif A1 A1 xx 1 1 1 6 20 1 1 6 12 1 1 20 damage synergy 1 30 20 1 128 pois 18 10 15 20 23 26 40 10 15 20 23 26 (skill('Poison Explosion'.blvl)+skill('Poison Nova'.blvl))*par8 50 10 10 10 384 3000
Corpse Explosion 74 nec corpse explosion 17 55 ln34 1 necromancer_corpse_cast necromancer_corpseexp_1 21 32 corpseexplosion explodingarrowexp redlightmissile 1 0 none SC SC xx 1 1 1 1 3 1 necromancer_corpse_cast 6 20 Teeth 1 8 15 1 1 par1 % target hp min damage par2 % target hp max damage par5 % damage to do as elemental 70 % of base monster HP min damage 120 % of base monster HP max damage 8 radius (half squares) 1 additional radius/level (half squares) 50 % damage to do as elemental 1 8 fire 384 3000
Clay Golem 75 nec clay golem 56 item_slow dm34 damagedinmelee 27 velocitypercent skill('Golem Mastery'.dm34) tohit skill('Golem Mastery'.ln56) + (lvl*par8) damagepercent par2 * (lvl - 1) + (skill('FireGolem'.blvl)*skill('FireGolem'.par8)) armorclass skill('IronGolem'.blvl)*skill('IronGolem'.par8) ClayGolem golem 1 S1 necromancer_golem_cast 1 5 none SC SC S1 4 6 20 1 8 15 3 1 1 (100+(par1 * (lvl - 1)))*(100+skill('Golem Mastery'.ln12) + (skill('BloodGolem'.blvl)*skill('BloodGolem'.par8)))/100-100 hp % adjustment 35 % HP bonus per level 35 % Damage bonus per level 0 slow effect min 75 slow effect max 20 clay golem attack synergy 1 8 384 3000
Iron Maiden 76 nec iron maiden 30 3 ironmaiden ln34 ln12 domeleedamage 4 necromancer_curse_cast 18 30 curseironmaiden cursecast 1 9 none SC SC S2 4 1 12 20 Amplify Damage 1 8 5 0 1 ln56 % damage to return ln56/4 % damage to return vs. players ln56/4 % damage to return other 7 radius 0 radius per level 300 duration 60 additional duration/level 200 % damage returned to accursed 25 % additional returned/level 1 16385 141 8 512 8000
Terror 77 nec terror 30 2 terror ln34 ln12 necromancer_curse_cast 18 30 curseterror cursecast 1 0 none SC SC xx 4 1 necromancer_curse_cast 12 20 Weaken 1 8 7 0 1 4 radius 0 radius per level 200 duration 25 additional duration/level 24 Distance to run 2 Distance per level 1 8 512 8000
Bone Wall 78 nec bone wall 60 bonewallmaker bonewall none S1 necromancer_bone_cast 22 1 0 none SC SC xx 4 1 necromancer_bone_cast 12 20 Bone Armor 1 8 17 0 1 (par1 * (lvl-1)) + ((skill('Bone Armor'.blvl)+skill('Bone Prison'.blvl))*par8) hp % adjustment par34 # of walls - 1 25 % additional HP per level 600 MAX duration 8 Max Monsters per wall 0 "Level, max monsters per wall" 10 life synergy 1 8 512 8000
Golem Mastery 79 nec golem mastery golem_mastery 1 0 none SC SC xx 12 20 Clay Golem 1 0 8 0 0 1 1 20 % HP bonus per level 20 % HP bonus per level 0 min velocity increase 40 max velocity increase 25 to hit 25 to hit/lvl 1 8 512 8000
Raise Skeletal Mage 80 nec raise skeletal mage 15 31 armorclass (lvl+skill('Skeleton Mastery'.lvl))*par5 maxhp skill('Skeleton Mastery'.lvl) * skill('Skeleton Mastery'.par1) * 256 necromage skeletonmage (lvl < 4) ?lvl:(2+lvl/3) S1 NecromageMissile skill('Skeleton Mastery'.lvl) + ((lvl < 4)?0:((lvl-2)/2)) 1 necromancer_golem_cast 20 31 corpseexplosion 1 0 none SC SC xx 1 2 1 12 20 Raise Skeleton 1 8 8 1 1 (lvl < 4) ? 0 : (par2 * (lvl - 3)) hp % adjustment 7 hp%/lvl 10 ac/lvl 1 8 512 8000
Confuse 81 nec confuse 61 2 confuse ln34 ln12 necromancer_curse_cast 18 30 curseconfuse cursecast 1 0 none SC SC xx 4 1 necromancer_curse_cast 18 20 Dim Vision 1 8 13 0 1 6 radius 1 radius per level 250 duration 50 additional duration/level 1 8 640 16000
Life Tap 82 nec life tap 30 3 lifetap ln34 ln12 damagedinmelee 5 damagedbymissile 5 necromancer_curse_cast steallife 18 30 cursereversevampire cursecast 1 4 none SC SC S2 4 1 necromancer_curse_cast 18 20 Iron Maiden 1 8 9 0 1 ln56 hp % returned 4 radius 1 radius per level 400 duration 60 additional duration/level 50 % damage healed 0 % additional heal/level 1 8 640 16000
Poison Explosion 83 nec poison explosion 17 63 poisonexplosioncloud ln34 1 necromancer_corpse_cast necromancer_corpseexp_1 21 33 poisoncorpseexplosion poisonexplosioncloud greenlightmissile 1 0 none SC SC xx 1 1 1 1 3 1 necromancer_corpse_cast 18 20 Poison Dagger Corpse Explosion 1 8 8 0 1 15 damage synergy 1 4 pois 8 2 4 6 8 10 24 2 4 6 8 10 (skill('Poison Dagger'.blvl)+skill('Poison Nova'.blvl))*par8 50 10 10 10 640 16000
Bone Spear 84 nec bone spear bonespear necromancer_bone_cast 19 bonespear bonecast 1 6 none SC SC xx 1 necromancer_bone_cast 18 20 Corpse Explosion 1 1 6 28 1 1 7 damage synergy 1 8 mag 16 8 9 12 18 24 24 8 9 13 19 25 (skill('Bone Wall'.blvl)+skill('Bone Prison'.blvl)+skill('Teeth'.blvl)+skill('Bone Spirit'.blvl))*par8 640 16000
BloodGolem 85 nec bloodgolem 56 domeleedamage 23 damagedinmelee 26 damagedbymissile 26 velocitypercent skill('Golem Mastery'.dm34) tohit skill('Golem Mastery'.ln56)+skill('Clay Golem'.blvl)*skill('Clay Golem'.par8) damagepercent par4 * (lvl - 1) + (skill('FireGolem'.blvl)*skill('FireGolem'.par8)) armorclass skill('IronGolem'.blvl)*skill('IronGolem'.par8) BloodGolem golem 1 S1 necromancer_golem_cast 1 5 none SC SC xx 4 18 20 Clay Golem 1 8 25 4 1 1 skill('Golem Mastery'.ln12) hp % adjustment 75 min % life stolen 150 max % life stolen 30 % stolen life xfer to caster 35 %additional golem damage per level 0 % damage on golem xfered to caster 25 % caster healing xfered to golem 5 blood golem life synergy 1 8 640 16000
Attract 86 nec attract 18 59 2 attract ln34 ln12 necromancer_curse_cast 18 30 curseattract cursecast 1 0 none SC SC xx 4 1 1 24 20 Confuse 1 8 17 0 1 9 radius 0 radius per level 300 duration 90 additional duration/level 1 8 768 32000
Decrepify 87 nec decrepify 30 3 decrepify ln34 ln12 velocitypercent par5 damagepercent par5 damageresist par5 attackrate par5 necromancer_curse_cast 18 30 cursedecrepify cursecast 1 9 none SC SC S2 4 1 necromancer_curse_cast 24 20 Terror 1 8 11 0 1 6 radius 0 radius per level 100 duration 15 additional duration/level -50 "% slowed, -dam, -dam resist" 1 8 768 32000
Bone Prison 88 nec bone prison 19 62 bonewall none NU necromancer_bone_cast 22 1 10 none SC SC xx 4 1 1 1 necromancer_bone_cast 24 20 Bone Wall Bone Spear 1 8 27 -1 1 (par1 * (lvl-1)) + ((skill('Bone Armor'.blvl)+skill('Bone Wall'.blvl))*par8) hp % adjustment 25 % additional HP per level 600 MAX duration 8 life synergy 1 8 768 32000
Summon Resist 89 nec summon resist summonresist passive_summon_resist dm12 1 6 none SC SC xx 24 20 Golem Mastery 1 1 8 44 -3 1 1 20 min % resist 75 max % resist 1 8 768 32000
IronGolem 90 nec irongolem 20 57 thorns thorns_percent ln12 fade 16 velocitypercent skill('Golem Mastery'.dm34) tohit skill('Golem Mastery'.ln56)+skill('Clay Golem'.blvl)*skill('Clay Golem'.par8) armorclass lvl*par8 damagepercent (skill('FireGolem'.blvl)*skill('FireGolem'.par8)) IronGolem golem 1 S1 1 necromancer_golem_cast 23 1 5 none SC SC xx 4 1 1 24 20 BloodGolem 1 8 35 0 1 1 skill('Golem Mastery'.ln12) + (skill('BloodGolem'.blvl)*skill('BloodGolem'.par8)) hp % adjustment 150 % thorns damage back (level 2) 15 % thorns damage back per level 35 iron golem armor synergy 1 8 768 32000
Lower Resist 91 nec lower resist 30 3 lowerresist ln34 ln12 fireresist -dm56 lightresist -dm56 coldresist -dm56 poisonresist -dm56 necromancer_curse_cast 18 30 curselowerresist cursecast 1 8 none SC SC S2 4 1 necromancer_curse_cast 30 20 Life Tap Decrepify 1 8 22 0 1 7 radius 1 radius per level 500 duration 50 additional duration/level 25 min % resist lower 70 max % resist lower 1 8 896 64000
Poison Nova 92 nec poison nova 22 poisonnova necromancer_poison_cast 25 poisonnova 1 3 none SC SC xx 1 necromancer_poison_cast 30 20 Poison Explosion 1 8 20 0 1 10 damage synergy 1 4 pois 16 4 6 9 14 16 29 4 6 9 14 16 (skill('Poison Dagger'.blvl)+skill('Poison Explosion'.blvl))*par8 50 896 64000
Bone Spirit 93 nec bone spirit 10 bonespirit bonespirit necromancer_bone_cast 18 bonespirit 1 7 none SC SC xx 1 4 necromancer_bone_cast 30 20 Bone Spear 1 1 7 24 1 1 0 + dmg% 6 damage synergy 1 8 mag 20 16 17 18 19 20 30 17 18 19 20 21 (skill('Bone Wall'.blvl)+skill('Bone Prison'.blvl)+skill('Teeth'.blvl)+skill('Bone Spear'.blvl))*par8 896 64000
FireGolem 94 nec firegolem 56 fireresist 100 - dm12 item_absorbfire_percent dm12 firemindam edmn firemaxdam edmx velocitypercent skill('Golem Mastery'.dm34) tohit skill('Golem Mastery'.ln56)+skill('Clay Golem'.blvl)*skill('Clay Golem'.par8) armorclass skill('IronGolem'.blvl)*skill('IronGolem'.par8) FireGolem golem 1 S1 holy fire "min(ln56,30)" necromancer_golem_cast 1 6 none SC SC xx 4 1 30 20 IronGolem 1 8 50 10 1 1 skill('Golem Mastery'.ln12) + (skill('BloodGolem'.blvl)*skill('BloodGolem'.par8)) hp % adjustment 25 min % fire absorbtion 100 max % fire absorbtion 100 % damage increase 35 % damage increase per level 8 Holy Fire Aura level 1 Plus Holy Fire Aura level 6 fire golem damage synergy 1 8 fire 10 9 10 11 12 13 27 10 11 12 13 14 896 64000
Revive 95 nec revive 21 58 damagepercent skill('Skeleton Mastery'.lvl) * skill('Skeleton Mastery'.par4) velocitypercent par5 revive lvl NU 1 necromancer_revive_cast necromancer_revive_target 24 revivemedium revivesmall revivelarge 1 6 none SC SC xx 4 1 3 1 30 20 Raise Skeletal Mage IronGolem 1 8 45 0 1 par1+skill('Skeleton Mastery'.lvl) * skill('Skeleton Mastery'.par3) hp % adjustment ln34 duration 200 additional hp percent 4500 Duration 0 additional duration/level 50 Velocity bonus for revived 1 8 896 64000
Sacrifice 96 pal sacrifice 29 64 paladin_sacrifice 1 34 blood1 3 blood1-bigblood1 1 7 h2h mele A1 A1 xx 2 1 1 1 1 1 1 20 1 0 8 0 0 1 ln12+skill('Redemption'.blvl)*par8+skill('Fanaticism'.blvl)*par7 damage % par3 damage self % 180 Percent damage 15 percent damage per level 8 percent damage to self 5 damage synergy 15 damage synergy 1 20 7 8 128 256 1000
Smite 97 pal smite 150 weapon_2hs_large_1 1 1 9 h2h 4 shld S1 S1 xx 1 1 1 1 1 20 1 1 8 2 0 ln34 damage % "min(250,ln12)" stunlen 15 Stun Length 5 additional frames/level 15 Percent bonus damage 15 percent damage per level 1 8 8 256 1000
Might 98 pal might 65 73731 might might ln12 damagepercent ln34 1 1 9 none xx 1 20 1 8 0 0 1 1 1 50 16 radius 2 additional radius per level 40 % additional damage 10 % additional damage/level 1 8 256 1000
Prayer 99 pal prayer 65 73731 prayer prayer ln12 hitpoints edns 1 7 none xx 1 20 1 4 16 3 1 1 1 50 16 radius 2 additional radius per level 1 8 2 1 1 2 2 3 256 1000
Resist Fire 100 pal resist fire 65 73731 resistfire resistfire ln12 fireresist dm34 maxfireresist skill('Resist Fire'.blvl) passive_resistfire maxfireresist skill('Resist Fire'.blvl)/2 1 1 8 none xx 1 20 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 35 min % resist 150 max % resist 1 8 256 1000
Holy Bolt 101 pal holy bolt holybolt paladin_holybolt_cast cast_undead holybolt 1 7 none SC SC xx 1 1 1 paladin_holybolt_cast cast_undead 6 20 1 1 4 32 1 1 ln12 * (100 + skill('Prayer'.blvl) * par7) / 100 min heal ln34 * (100 + skill('Prayer'.blvl) * par7) / 100 max heal 1 min hitpoints healed 2 hitpoints healed per level 6 max hitpoints healed 4 hitpoints healed per level 15 heal synergy 50 damage synergy 1 8 mag 8 8 10 13 16 20 16 8 11 15 18 23 (skill('Blessed Hammer'.blvl)+skill('Fist of the Heavens'.blvl))*par8 384 3000
Holy Fire 102 pal holy fire 66 42883 holyfire ln12 firemindam enms*par5/256 firemaxdam exms*par5/256 1 0 none xx 6 20 Might 0 8 0 0 1 1 1 50 6 radius 1 additional radius per level 6 damage to attack multiplier 6 damage synergy 18 damage synergy 1 16385 7 fire 2 1 2 3 5 7 6 1 2 3 5 7 skill('Resist Fire'.blvl)*par8+skill('Salvation'.blvl)*par7 384 3000
Thorns 103 pal thorns 65 73731 thorns thorns ln12 thorns_percent ln34 1 9 none xx 6 20 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 250 % damage bounced back 40 % additional bounce back/level 1 8 384 3000
Defiance 104 pal defiance 65 73731 defiance defiance ln12 skill_armor_percent ln34 1 1 4 none xx 6 20 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 70 % additional AC 10 % additional AC/level 0 ? 0 ? 1 8 384 3000
Resist Cold 105 pal resist cold 65 73731 resistcold resistcold ln12 coldresist dm34 maxcoldresist skill('Resist Cold'.blvl) passive_resistcold maxcoldresist skill('Resist Cold'.blvl)/2 1 1 6 none xx 6 20 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 35 min % resist 150 max % resist 0 ? 0 ? 1 8 384 3000
Zeal 106 pal zeal 37 13 1 paladin_zeal 53 21 1 2 h2h mele A1 A1 xx 1 1 1 12 20 Sacrifice 1 1 8 2 0 "min((par5 + lvl -1), par6)" max targets ((lvl < 5) ? 0 : ((lvl-4) * par4) )+skill('Sacrifice'.blvl)*par8 damage % 100 % frame rollback 0 % damage percent 6 damage % per level 2 Targets 5 Max targets 12 damage synergy 1 10 10 8 128 512 8000
Charge 107 pal charge 31 67 1 paladin_charge 25 37 1 2 none mele SQ A1 xx 4 8 1 1 1 1 12 20 Smite 1 1 8 9 0 ln34+(skill('Vigor'.blvl)+skill('Might'.blvl))*par8 damage % elem conversion% 150 percent increase in velocity 0 ? 100 plus % damage 25 plus % dam per level 2 Trails 0 ? 20 damage synergy 1 50 15 8 128 512 8000
Blessed Aim 108 pal blessed aim 65 73731 blessedaim blessedaim ln12 item_tohit_percent ln34 penetrate item_tohit_percent skill('Blessed Aim'.blvl) * par8 1 1 6 none xx 12 20 Might 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 75 % attack# bonus 15 % additional attack# bonus 0 ? 0 ? 5 passive to hit bonus 1 8 512 8000
Cleansing 109 pal cleansing 65 73731 cleansing cleansing ln12 item_poisonlengthresist 100-dm34 hitpoints skill('Prayer'.edns) 1 3 none xx 12 20 Prayer 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 30 min % reduction 90 max % reduction 0 ? 0 ? 1 8 512 8000
Resist Lightning 110 pal resist lightning 65 73731 resistlight resistlight ln12 lightresist dm34 maxlightresist skill('Resist Lightning'.blvl) passive_resistltng maxlightresist skill('Resist Lightning'.blvl)/2 1 1 6 none xx 12 20 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 35 min % resist 150 max % resist 0 ? 0 ? 1 8 512 8000
Vengeance 111 pal vengeance 35 2 1 1 3 h2h mele A1 A1 xx 1 1 1 1 18 20 Zeal 1 1 6 16 1 1 ln12+skill('Resist Fire'.blvl)*par8+skill('Salvation'.blvl)*par7 fire damage% ln12+skill('Resist Cold'.blvl)*par8+skill('Salvation'.blvl)*par7 cold damage% ln12+skill('Resist Lightning'.blvl)*par8+skill('Salvation'.blvl)*par7 ltng damage% 70 percent damage 6 percent damage per level 0 ? 0 ? 0 ? 0 ? 2 damage synergy 10 damage synergy 1 20 10 8 128 cold 30 15 15 15 640 16000
Blessed Hammer 112 pal blessed hammer 73 blessedhammer paladin_holybolt_cast 35 blessedhammer 1 2 none SC SC xx 1 paladin_holybolt_cast 18 20 Holy Bolt 1 1 6 20 1 1 4 Concentration influence in 8ths 0 ? 0 ? 0 ? 0 ? 0 ? 14 damage synergy 1 8 mag 12 8 10 12 13 14 16 8 10 12 13 14 (skill('Vigor'.blvl)+skill('Blessed Aim'.blvl))*par8 640 16000
Concentration 113 pal concentration 65 73731 concentration concentration ln12 damagepercent ln34 skill_concentration par5 1 1 6 none xx 18 20 Blessed Aim 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 60 % additional damage 15 $ additional damage/level 20 percent chance that attack will not be interrupted 0 ? 1 8 640 16000
Holy Freeze 114 pal holy freeze 81 303747 holywind holywindcold ln12 velocitypercent -dm34 attackrate -dm34 other_animrate -dm34 coldmindam enms*par5/256 coldmaxdam exms*par5/256 1 8 none xx 18 20 Holy Fire 0 8 0 0 1 1 1 50 6 radius 1 additional radius per level 25 min% slowing 60 max% slowing 5 damage to attack multiplier 0 ? 7 damage synergy 15 damage synergy 1 16385 8 cold 2 1 2 3 4 5 3 1 2 3 4 5 skill('Resist Cold'.blvl)*par8+skill('Salvation'.blvl)*par7 640 16000
Vigor 115 pal vigor 65 73731 stamina stamina ln12 staminarecoverybonus ln34 skill_staminapercent ln34 velocitypercent dm56 1 1 1 none xx 18 20 Cleansing Defiance 0 8 0 0 1 1 1 50 16 radius 3 additional radius per level 50 % faster stamina recovery and max stamina 25 % additional stamina recovery and max stamina 7 min % speed increase 50 max % speed increase 1 8 640 16000
Conversion 116 pal conversion 32 79 conversion ln12 1 1 0 h2h mele A1 A1 xx 1 1 1 1 24 20 Vengeance 1 1 8 4 0 1 dm34 chance to convert 400 duration of conversion (frames) 0 additional frames/level 0 min % chance convert 50 max % chance convert 0 ? 0 ? 1 8 128 768 32000
Holy Shield 117 pal holy shield 36 18 holyshield ln12 toblock dm56 ln34+skill('Defiance'.blvl)*par8 paladin_holyshield 1 3 none shld SC SC xx 1 24 20 Charge Blessed Hammer 1 8 35 0 1 1 ln34+skill('Defiance'.blvl)*par8 ac bonus to shield 750 duration of shield 625 duration/level 25 AC bonus 15 AC bonus per level 10 min % ToBlock Add 40 max % ToBlockAdd 15 armor synergy 1 8 3 2 3 4 4 4 6 2 3 4 4 4 768 32000
Holy Shock 118 pal holy shock 66 42883 holyshock ln12 lightmindam 1 lightmaxdam exms*par5/256 1 4 none xx 24 20 Holy Freeze 0 8 0 0 1 1 1 50 6 radius 1 additional radius per level 6 damage to attack multiplier 0 ? 4 damage synergy 12 damage synergy 1 16385 8 ltng 1 10 6 8 10 12 15 skill('Resist Lightning'.blvl)*par8+skill('Salvation'.blvl)*par7 768 32000
Sanctuary 119 pal sanctuary 66 59270 sanctuary ln12 item_undeaddamage_percent ln34 item_undead_tohit ln56 skill_bypass_undead 1 cast_undead sanctuarybolt 1 0 none xx 1 24 20 Thorns Holy Freeze 1 8 1 0 1 1 1 50 5 radius 1 additional radius per level 150 % damage to undead 30 % damage per level 100 att vs. undead 50 att vs. undead per level 7 damage synergy 1 11 157 8 mag 8 4 4 5 5 6 16 4 5 6 6 7 skill('Cleansing'.blvl)*par8 768 32000
Meditation 120 pal meditation 65 73729 meditation meditation ln12 manarecoverybonus ln34 hitpoints skill('Prayer'.edns) 1 7 none xx 24 20 Cleansing 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 300 % boost to mana recovery 25 % additional boost/level 0 ? 0 ? 1 8 768 32000
Fist of the Heavens 121 pal fist of the heavens 80 fistoftheheavensdelay handofgod 42375 20 paladin_handofgod_cast 36 fistoftheheavensdelay 1 7 none SC SC xx 4 1 1 1 4 30 20 Blessed Hammer Conversion 25 1 1 8 25 0 1 ln34 min heal ln56 max heal ln12 # bolts 6 holy bolts to release 1 holy bolts per level 1 min hitpoints healed 2 hitpoints healed per level 6 max hitpoints healed 2 hitpoints healed per level 7 damage synergy 1 8 ltng 150 15 30 45 55 65 200 15 30 45 55 65 skill('Holy Shock'.blvl)*par8 896 64000
Fanaticism 122 pal fanaticism 65 73731 fanaticism fanaticism ln12 attackrate dm34 item_tohit_percent toht damagepercent ln56/2 damagepercent ln56 1 1 8 none S1 30 20 Concentration 0 8 0 0 1 1 1 50 11 radius 1 additional radius per level 10 min% boost 40 max% boost 50 % damage boost 17 % damage bonus per level 1 40 5 8 896 64000
Conviction 123 pal conviction 66 42371 conviction conviction ln12 skill_armor_percent -dm56 fireresist "-min(ln34,150)" coldresist "-min(ln34,150)" lightresist "-min(ln34,150)" 1 6 none mele xx 5 1 30 20 Sanctuary 0 8 0 0 1 1 1 50 20 radius 0 additional radius per level 30 % resist reduction 5 % resist reduction per level 40 min % AC reduction 100 max % AC reduction 1 8 896 64000
Redemption 124 pal redemption 82 4354 redemption ln12 redemption 1 0 none xx 30 20 Vigor 0 8 0 0 1 1 1 50 dm34 chance to redeem ln56 hp gain ln56 mana gain 16 radius 0 additional radius per level 10 min % chance redeem 100 max % chance redeem 25 HP and mana gained 5 HP and mana gained/level 1 8 896 64000
Salvation 125 pal salvation 65 73731 resistall resistall ln12 fireresist dm34 coldresist dm34 lightresist dm34 1 1 8 none xx 30 20 0 8 0 0 1 1 1 50 16 radius 2 additional radius per level 50 min % resist 120 max % resist 1 8 896 64000
Bash 126 bar bash 32 2 barbarian_grunt_small_1 1 1 7 h2h mele A1 A1 xx 1 1 1 1 1 20 1 1 8 2 0 1 ln12+skill('Stun'.blvl)*par8 damage% ln34 post dmg add attack rate bonus 50 Damage % base 5 Damage % per level 1 Min Damage 1 Min Damager per level 5 to hit synergy 5 damage synergy 1 15+lvl*5+skill('Concentrate'.blvl)*par7 8 112 8 128 256 1000
Sword Mastery 127 bar sword mastery swordmastery swor passive_mastery_melee_th ln12 passive_mastery_melee_dmg ln34 passive_mastery_melee_crit dm56 1 0 none SC SC xx 1 1 20 0 8 0 0 1 1 1 28 Attack % base 8 Attack % per level 28 Damage % base 5 damage % per level 0 critical% 35 critical% 1 8 256 1000
Axe Mastery 128 bar axe mastery axemastery axe passive_mastery_melee_th ln12 passive_mastery_melee_dmg ln34 passive_mastery_melee_crit dm56 1 0 none SC SC xx 1 1 20 0 8 0 0 1 1 1 28 Attack % base 8 Attack % per level 28 Damage % base 5 damage % per level 0 critical% 35 critical% 1 8 256 1000
Mace Mastery 129 bar mace mastery macemastery blun passive_mastery_melee_th ln12 passive_mastery_melee_dmg ln34 passive_mastery_melee_crit dm56 1 0 none SC SC xx 1 1 20 0 8 0 0 1 1 1 28 Attack % base 8 Attack % per level 28 Damage % base 5 damage % per level 0 critical% 35 critical% 1 8 256 1000
Howl 130 bar howl 22 howl terror barbarian_howl_1 25 howl par1 * (lvl-1) velocity adder 1 0 none SC SC xx 1 1 1 20 1 8 4 0 1 par1 * (lvl-1) velocity adder 2 Velocity/level increase 1 Plev+Slev+n 24 Distance to retreat 5 Distance per level 75 Time to retreat 25 Time per level 1 8 256 1000
Find Potion 131 bar find potion 33 69 1 barbarian_findobject_1 barbarian_findheart_1 26 38 1 0 h2h SC SC xx 1 1 4 1 1 20 1 8 2 0 1 dm12 chance 0 min chance to find heart 100 max chance to find heart 30 Chance of finding a mana potion 10 Chance of finding rejuv 1 8 256 3000
Leap 132 bar leap 40 77 leapknockback dm12 1 barbarian_leap_1 29 43 1 0 none SQ A1 xx 13 1 6 20 1 1 8 2 0 1 ln34 knockback radius 4 min distance 30 max distance 4 Knockback range 1 knockback range per level 1 8 384 3000
Double Swing 133 bar double swing 70 1 27 39 par5 attack rate bonus 1 5 h2h 3 mele mele SQ A1 xx 11 1 1 1 1 1 6 20 Bash 1 0 5 8 -1 1 skill('Bash'.blvl)*par8 damage% post dmg add par5 attack rate bonus 50 Attack rate bonus 50 Attack rate bonus 10 damage synergy 1 15 5 8 128 384 3000
Pole Arm Mastery 134 bar pole arm mastery polearmmastery pole passive_mastery_melee_th ln12 passive_mastery_melee_dmg ln34 passive_mastery_melee_crit dm56 1 0 none SC SC xx 1 6 20 0 8 0 0 1 1 30 Attack % base 8 Attack % per level 28 Damage % base 5 damage % per level 0 critical% 35 critical% 1 8 384 3000
Throwing Mastery 135 bar throwing mastery throwingmastery thro passive_mastery_throw_th ln12 passive_mastery_throw_dmg ln34 passive_mastery_throw_crit dm56 1 0 none SC SC xx 1 6 20 0 8 0 0 1 1 30 Attack % base 8 Attack % per level 28 Damage % base 5 damage % per level 0 critical% 35 critical% 1 8 384 3000
Spear Mastery 136 bar spear mastery spearmastery spea passive_mastery_melee_th ln12 passive_mastery_melee_dmg ln34 passive_mastery_melee_crit dm56 1 0 none SC SC xx 1 6 20 0 8 0 0 1 1 30 Attack % base 8 Attack % per level 28 Damage % base 5 damage % per level 0 critical% 35 critical% 1 8 384 3000
Taunt 137 bar taunt 71 34179 taunt item_tohit_percent ln12 damagepercent ln34 barbarian_taunt_1 1 0 none SC SC S1 1 1 1 6 20 Howl 1 8 3 0 1 -5 % to hit for target -2 % to hit/level -5 % damage for target -2 % damage/level 1 8 384 3000
Shout 138 bar shout 68 shout shout shout ln34+(skill('Battle Orders'.blvl)+skill('Battle Command'.blvl))*par8 skill_armor_percent ln12 barbarian_shout_1 25 shout 1 8 none SC SC SC 1 6 20 Howl 1 8 6 0 1 100 % AC bonus for friendlies 10 % AC bonus per level 500 duration 250 duration/level 125 duration synergy 1 8 384 3000
Stun 139 bar stun 32 2 barbarian_grunt_small_1 1 1 0 h2h mele A1 A1 xx 1 1 1 1 1 12 20 Bash 1 1 8 2 0 1 skill('Bash'.blvl)*par8 30 Frames the target is stunned 5 additional frames/level 5 stun synergy 5 to hit synergy 8 damage synergy 1 10+lvl*5+skill('Concentrate'.blvl)*par7 96 8 128 stun 30 5 5 2 skill('War Cry'.blvl)*par6 512 8000
Double Throw 140 bar double throw 74 11 42 1 5 rng 3 thro thro SQ A1 xx 15 1 1 12 20 Double Swing 1 6 1 8 1 0 1 skill('Double Swing'.blvl)*par8 damage% 0 ? 0 ? 8 damage synergy 1 20 10 8 128 512 8000
Increased Stamina 141 bar increased stamina increasedstamina skill_passive_staminapercent ln12 1 0 none SC SC xx 1 12 20 0 8 0 0 1 1 30 % stamina increase 15 % stamina increase per level 1 8 512 8000
Find Item 142 bar find item 34 72 1 barbarian_findobject_1 barbarian_findheart_1 28 40 1 0 h2h SC SC xx 1 1 5 1 12 20 Find Potion 1 8 7 0 1 dm12 chance 5 min chance to find heart 60 max chance to find heart 30 % chance high quality 5 % chance magic item 1 8 512 8000
Leap Attack 143 bar leap attack 41 78 bash 1 barbarian_leapattack_1 30 44 1 6 none mele SQ A1 xx 14 1 1 18 20 Leap 1 1 8 9 0 1 ln34+skill('Leap'.blvl)*par8 4 min distance 30 max distance 100 Min % damage bonus 30 % damage bonus 4 Knockback range 2 knockback range per level 10 damage synergy 1 50 15 8 128 640 16000
Concentrate 144 bar concentrate 32 2 doubledamage1 concentrate skill_armor_percent ln34 barbarian_grunt_small_1 1 1 0 h2h mele A1 A1 xx 1 1 1 1 18 20 Stun 1 1 8 2 0 ln12+skill('Bash'.blvl)*par8+skill('Battle Orders'.blvl)*par7 damage% post dmg add attack rate bonus skill('Berserk'.blvl) convert% 70 Damage % base 5 Damage % per level 100 % AC bonus 10 % AC bonus per level 10 damage synergy 5 damage synergy 1 60 10 8 128 mag 640 16000
Iron Skin 145 bar iron skin ironskin skill_armor_percent ln12 1 0 none SC SC xx 1 18 20 0 8 0 0 1 1 30 % AC bonus 10 % AC bonus per level 1 8 640 16000
Battle Cry 146 bar battle cry 68 battlecry 98304 battlecry ln12 skill_armor_percent ln34 damagepercent ln56 barbarian_battlecry_1 25 battlecry 1 9 none SC SC xx 1 18 20 Taunt 1 8 5 0 1 300 duration 60 duration/level -50 % AC bonus -2 % AC bonus per level -25 $ damage bonus -1 % damage bonus per level 1 8 640 16000
Frenzy 147 bar frenzy 9 frenzy par7 velocitypercent dm34 attackrate dm56 1 39 1 3 h2h 3 mele mele SQ A1 xx 11 1 1 1 1 24 20 Double Throw 1 1 7 3 0 ln12+(skill('Double Swing'.blvl) + skill('Taunt'.blvl))*par8 damage% skill('Berserk'.blvl) convert% 90 Damage % base 5 Damage % per level 20 min % run speed increase 200 max % run speed increase 0 min % attack speed increase 50 max % attack speed increase 150 duration 8 damage synergy 1 100 7 8 128 mag 768 32000
Increased Speed 148 bar increased speed increasedspeed velocitypercent dm12 1 0 none SC SC xx 1 24 20 Increased Stamina 0 8 0 0 1 1 7 min % speed increase 50 max % speed increase 1 8 768 32000
Battle Orders 149 bar battle orders 68 battleorders battleorders battleorders ln12+(skill('Shout'.blvl)+skill('Battle Command'.blvl))*par8 item_maxmana_percent ln34 item_maxhp_percent ln34 skill_staminapercent ln34 barbarian_battleorders_1 25 battleorders 1 9 none SC SC xx 1 24 20 Shout 1 8 7 0 1 750 duration 250 duration/level 35 Base % increase 3 Increase per level 125 duration synergy 1 50 10 8 768 32000
Grim Ward 150 bar grim ward 33 75 grimwardmediumstart grimwardsmallstart grimwardlargestart 2 terror par6 ln12 barbarian_grimward_1 26 41 grimwardmediumstart grimwardsmallstart grimwardlargestart 1 0 h2h SC SC xx 1 1 6 1 24 20 Find Item 1 8 4 0 1 ln34 ward duration 3 radius 1 radius/level 1000 duration 0 duration/level 10 Distance to run 60 Monster scare duration 1 8 768 32000
Whirlwind 151 bar whirlwind 38 76 whirlwind 1 barbarian_whirlwind 31 45 1 3 none 2 mele SQ A1 xx 10 1 1 1 30 20 Leap Attack Concentrate 1 1 7 25 1 ln12 damage% -50 Damage percent per attack 8 Damage percent per level 1 Attacks per tick 1 5 8 128 896 64000
Berserk 152 bar berserk 39 2 berserk "par4-min(((110*lvl)/(lvl+6)*(par4-par3)/100),(par4-par3))" damageresist par5 armor_override_percent -100 barbarian_grunt_large_1 1 1 4 h2h mele A1 A1 xx 1 1 1 1 30 20 Concentrate 1 1 8 4 0 1 ln12+(skill('Howl'.blvl) + skill('Shout'.blvl))*par8 damage% "par4-min(((110*lvl)/(lvl+6)*(par4-par3)/100),(par4-par3))" duration 100 convert% 150 Damage % base 15 Damage % per level 25 min vulnerable duration 75 max vulerable duration 0 damage resist bonus 10 damage synergy 1 100 15 8 128 mag 896 64000
Natural Resistance 153 bar natural resistance naturalresistance fireresist dm12 lightresist dm12 coldresist dm12 poisonresist dm12 1 0 none SC SC xx 1 30 20 Iron Skin 0 8 0 0 1 1 0 min % resistance bonus 80 max % resistance bonus 1 8 896 64000
War Cry 154 bar war cry 68 warcry barbarian_warcry_1 warcry 25 warcry 1 9 none SC SC xx 1 30 20 Battle Cry Battle Orders 1 8 10 1 1 25 stun length 5 stun length per level 6 damage synergy 1 8 20 6 7 8 9 10 30 6 7 8 9 10 (skill('Howl'.blvl)+skill('Taunt'.blvl)+skill('Battle Cry'.blvl))*par8 896 64000
Battle Command 155 bar battle command 68 battlecommand battlecommand battlecommand ln12+(skill('Shout'.blvl)+skill('Battle Orders'.blvl))*par8 item_allskills 1 barbarian_command_1 25 battlecommand 1 9 none SC SC xx 1 30 20 Battle Orders 1 8 11 0 1 125 duration 250 duration/level 125 duration synergy 1 8 896 64000
Fire Hit 156 42 83 1 0 none SQ SQ xx 1 1 1 1 base damage 5 extra per level 50 duration 50 duration/level 1 20 10 8 0
UnHolyBolt 157 85 unholybolt1 unholybolt1 1 0 none SQ SQ xx 1 1 1 8 0
SkeletonRaise 158 1 0 none SQ SQ xx 1 1 1 1 1 1 8 0
MaggotEgg 159 43 84 sandmaggotegg_hatch_1 46 1 0 none SQ SQ xx 1 1 "(lvl < 5) ? lvl : min(12,5+(lvl-5)/3)" # to spawn 1 8 0
ShamanFire 160 85 shafire1 shafire1 1 0 none SQ SQ xx 1 1 1 8 0
MagottUp 161 44 sandmaggot_emerge 32 dirt pile sand pile 1 0 none SQ SQ xx 1 1 1 8 0
MagottDown 162 45 86 sandmaggot_burrow 33 47 1 0 none SQ SQ xx 1 1 10 heal% 1 8 0
MagottLay 163 87 1 0 none SQ SQ xx 1 1 1 8 0
AndrialSpray 164 46 88 andarielspray 34 48 andarielspray 1 0 none SQ SQ xx 1 1 1 8 0
Jump 165 47 89 1 0 none SQ SQ xx 1 1 10 damage% 1 15 5 8 0
Swarm Move 166 48 90 35 49 1 0 none SQ SQ xx 1 1 11 do frame 19 stop frame 1 8 0
Nest 167 49 91 36 50 1 0 none SQ SQ xx 1 1 1 8 0
Quick Strike 168 50 92 raven1 1 0 none SQ SQ xx 1 1 1 1 8 0
VampireFireball 169 vampirefireball vampirefireball 1 0 none SQ SQ SC 1 1 1 8 0
VampireFirewall 170 24 vampirefirewallmaker vampirefirewall 26 vampirefirewallmaker 1 0 none SQ SQ SC 1 1 1 0
VampireMeteor 171 28 vampiremeteorcenter ln12 sorceress_meteor fire_cast_2 28 vampiremeteorcenter 1 0 none SQ SQ SC 1 1 ln12 5 radius of explosion 1 radius/level 30 Frames of fire 15 Frames of fire per level 1 8 fire 5 5 5 5 5 5 10 5 5 5 5 5 0
GargoyleTrap 172 93 shafire3 51 shafire3 1 0 none SQ SQ xx 18 1 1 1 8 0
SpiderLay 173 23 spiderlay slowed 300 velocitypercent -100 1 0 none SQ SQ A2 1 1 20 frames to apply aura to target 30 Slow Duration/Level 75 Duration/Level 1 8 0
VampireHeal 174 1 0 none SQ SQ SC 1 1 1 8 0
VampireRaise 175 1 0 none SQ SQ SC 1 1 1 1 1 1 1 8 0
Submerge 176 51 94 37 52 1 0 none SQ SQ xx 1 1 1 8 0
FetishAura 177 111 ln12 necromancer_curse_1 53 curseeffectred 3 density 1 0 none SQ SQ SC 1 1 10 Base stat mod 50 Duration/Level 5 Stat Mod/Level 10 Base Range ( + 2/Lvl ) 1 8 0
FetishInferno 178 53 95 fetishinferno1 54 fetishinferno1 fetishinferno2 1 0 none SQ SQ A1 1 1 par1 length of fire 3 density 15 frame length 1 8 0
ZakarumHeal 179 96 receiving receiving healing 1 0 none SQ SQ S1 1 1 15+5*lvl min heal% 50 max heal % 15 ? 30 ? 5 ? 0 ? 1 8 0
Emerge 180 52 38 1 0 none SQ SQ S1 1 1 1 8 0
Resurrect 181 97 fallenshaman_resurrect_cast healing 39 1 0 none SQ SQ xx 1 1 1 1 1 1 1 1 8 0
Bestow 182 96 receiving 1 0 none SQ SQ xx 1 1 1 1 8 0
MissileSkill1 183 110 66 1 0 none SQ SQ S1 1 1 1 8 0
MonTeleport 184 98 sorceress_teleport teleport 1 0 none SQ SQ S1 1 1 1 8 0
PrimeLightning 185 monsterlight mephisto_lightning_cast monsterlight 1 0 none SQ SQ A2 1 1 1 8 0
PrimeBolt 186 17 chargedstrikebolt mephisto_chargedbolt_cast 23 chargedbolt 1 0 none SQ SQ A1 1 1 lvl+2 #bolts 1 8 0
PrimeBlaze 187 23 blaze blaze dm12 monster_cast_fire blaze 1 0 none SQ SQ A1 1 1 0 ? 60 duration/level 0 ? 0 ? 1 8 0
PrimeFirewall 188 24 vampirefirewallmaker vampirefirewall monster_cast_fire 26 vampirefirewallmaker 1 0 none SQ SQ A1 1 1 0 ? 0 ? 0 ? 0 ? 1 8 0
PrimeSpike 189 monglacialspike ln12 ln34 monster_cast_cold monglacialspike 1 0 none SQ SQ A1 1 1 4 radius 0 radius per level 25 freeze frames 10 freeze frames per level 1 8 0
PrimeIceNova 190 22 frostnova monster_cast_cold 25 frostnova 1 0 none SQ SQ A1 1 1 0 ? 0 ? 0 ? 0 ? 1 8 0
PrimePoisonball 191 8 poisonball mephisto_orb_cast 17 poisonball 1 0 none SQ SQ A1 1 1 3 #missiles 1 activate frame 0 ? 0 ? 0 ? 0 ? 1 8 0
PrimePoisonNova 192 99 primepoisoncloud 55 primepoisoncloud 1 0 none SQ SQ A1 1 1 lvl-1 #subloops 2 skip 0 ? 0 ? 0 ? 0 ? 1 8 0
DiabLight 193 53 152 diablight diablo_laser_cast 40 56 diablight 0 interval 1 0 none SQ SQ SC 1 1 1 par1 length of fire 2 density 40 frame length 0 ? 0 ? 0 ? 1 8 0
DiabCold 194 100 diablo_cold_cast 1 0 none SQ SQ S2 1 1 1 0 ? 200 Frames 0 ? 0 ? 1 8 mag 15 9 9 9 9 9 25 12 12 12 12 12 100 50 50 50 0
DiabFire 195 22 diabfire diablo_fire_cast 25 diabfire 1 0 none SQ SQ S1 1 1 1 0 ? 0 ? 0 ? 0 ? 1 8 0
FingerMageSpider 196 101 fingermagespider fingermagecurse ln12 manarecovery -par3 * lvl fingermage_bolt_cast_1 57 fingermagespider 1 0 none SQ SQ S1 1 1 lvl-1 #subloops 30 base frames 10 frames per level 24 unshifted damage per frame 0 ? 1 8 0
DiabWall 197 firestorm 102 diabwallmaker diablo_fire_cast 58 diabwallmaker 1 0 none SQ SQ S1 1 1 1 1 lvl #missiles 0 ? 0 ? 0 ? 0 ? 1 8 0
DiabRun 198 54 103 diablo_run 41 59 1 0 none SQ SQ xx 22 1 1 1 20 velocity 8 stop anim len 14 stop anim frame 5 start frame 13 repeat on frame 16 repeat len 6 repeat back frame 1 5 8 17 9 9 9 9 9 33 9 9 9 9 9 0
DiabPrison 199 104 boneprison1 none diablo_boneprison_rise 1 0 none SQ SQ S3 1 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
PoisonBallTrap 200 trap poison ball left trap poison ball left 1 0 none SC SC S1 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 10 10 10 10 10 10 20 10 10 10 10 10 0
AndyPoisonBolt 201 andypoisonbolt andypoisonbolt 1 0 none SQ SQ A1 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
HireableMissile 202 110 66 1 0 none SQ SQ A1 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
DesertTurret 203 105 desertfireball 60 desertfireball 1 0 none SQ SQ xx 26 1 1 3 #missiles 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
ArcaneTower 204 106 lightningtowernova lightningorb_attack_1 61 lightningtowernova 1 0 none SQ SQ xx 27 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
MonBlizzard 205 28 monblizcenter monster_cast_cold ice_cast_2 28 monblizcenter 1 3 none SQ SQ S1 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Mosquito 206 55 107 42 62 1 0 none SQ SQ xx 34 1 1 4 min loops 7 max loops 25 heal pct 12 reset loop frame 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 2 2 2 2 2 2 4 2 2 2 2 2 2 3 3 3 3 3 4 3 3 3 3 3 200 0
CursedBallTrapRight 207 trap cursed skull right trap cursed skull right 1 0 none SC SC S2 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 10 5 5 5 5 5 20 5 5 5 5 5 0
CursedBallTrapLeft 208 trap cursed skull left trap cursed skull left 1 0 none SC SC S2 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 10 5 5 5 5 5 20 5 5 5 5 5 0
MonFrozenArmor 209 18 frozenarmor ln34 skill_armor_percent ln12 monster_cast_cold sorceress_frozenarmor ice_cast_3 1 0 none SQ SQ SC 1 1 30 % AC base 5 % AC per level 3600 Duration 300 Duration per level 30 Freeze Frames 3 Freeze Frames per level 1 8 1 0
MonBoneArmor 210 18 bonearmor bonearmor ln12*256 bonearmormax ln12*256 necromancer_bonearmor 1 0 none SQ SQ S1 1 1 20 damage absorbed 10 additional absorbed/level 0 ? 0 ? 0 ? 0 ? 1 8 0
MonBoneSpirit 211 10 monbonespirit monbonespirit monster_cast_fire 18 monbonespirit monbonespirit 1 0 none SQ SQ S1 1 1 15 Search range 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
MonCurseCast 212 112 ln34 ln12 18 30 curseeffectred cursecast 1 5 none SQ SQ S2 1 1 3 radius 1 radius per level 300 duration 60 additional duration/level 25 min % resist lower 70 max % resist lower -50 speed 1 8 0
HellMeteor 213 28 hellmeteordown 44 1 4 none SQ SQ A1 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
RegurgitatorEat 214 108 regurgitator_eat_1 63 corpseexplosion bigblood1 1 2 none SQ SQ S1 1 1 1 1 33 heal% of target hp 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
MonFrenzy 215 64 109 monfrenzy ln12 velocitypercent dm34 attackrate dm34 other_animrate dm34 1 3 none SQ SQ A2 1 1 1 1 1 200 duration 25 duration/level 30 min % speed increase 110 max % speed increase 0 ? 0 ? 1 10 10 8 128 0
QueenDeath 216 64 1 0 none SQ SQ xx 41 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Scroll of Identify 217 scroll of identify 113 0 none SC SC xx 1 1 5 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Book of Identify 218 book of identify 113 0 none SC SC xx 1 1 5 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Scroll of Townportal 219 scroll of townportal 113 0 none SC SC xx 1 1 5 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Book of Townportal 220 book of townportal 113 0 none SC SC xx 1 1 5 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Raven 221 dru raven 114 druidhawk raven "min(lvl,par2)" S1 druid_summon 1 0 none SC SC xx 1 4 1 20 1 1 8 6 0 1 1 0 hp adj ulvl + par1 + lvl raven lvl -2 monster level = your level + this + SkillLevel 5 Max ravens 0 ? 0 ? 12 Attacks 1 Attacks per level 1 100 15 8 2 1 1 1 1 1 4 1 1 1 1 1 256 1000
Plague Poppy 222 dru plague poppy 115 vine_beast plaguepoppy vine 1 S1 Vine Attack lvl druid_summon 1 0 none SC SC xx 1 4 1 20 1 1 8 8 0 1 1 par3 * (lvl-1) hp % ulvl * 3 / 4 + lvl vine lvl 3 Number of vines 1 vines per level 25 % more hitpoints per level 0 ? 0 ? 0 ? 1 pois 12 7 12 15 17 19 16 7 12 15 17 19 100 256 1000
Wearwolf 223 dru wearwolf 116 wolf 1000+skill('Shape Shifting'.ln12) skill_staminapercent par1 attackrate dm34 item_tohit_percent toht item_maxhp_percent par2+skill('Shape Shifting'.ln34) wolf_into wolf_undo 45 1 0 none SC SC xx 22 1 1 1 20 1 25 1 1 8 15 0 1 25 % stamina increase 25 % hitpoints increase 10 % min speed increase 80 % max speed increase 0 ? 0 ? 1 50 15 8 128 256 1000
Shape Shifting 224 dru shape shifting 1 0 none SC SC xx 1 1 20 Wearwolf 0 8 0 0 1 1 1000 Base duration of all forms 500 Bonus duration per level 20 % base hitpoint bonus 5 % hitpoints per level 0 ? 0 ? 1 8 256 1000
Firestorm 225 dru firestorm 117 firestormmaker druid_firecast_b druid_fire_cast_1 67 firestormmaker 1 0 none SC SC xx 1 1 druid_firecast_b druid_fire_cast_1 1 20 15 1 1 8 4 0 1 ln12 # missiles 3 Number of missiles 0 Number of missiles per level 0 ? 0 ? 0 ? 0 ? 23 damage synergy 1 2 fire 3 3 5 7 14 21 6 3 6 8 15 23 (skill('Molten Boulder'.blvl)+skill('Eruption'.blvl))*par8 256 1000
Oak Sage 226 dru oak sage 119 poisonresist 100 maxpoisonresist 100 oaksage totem 1 NU Oak Sage Aura lvl druid_summon 1 0 none SC SC xx 1 4 6 20 1 1 8 15 1 1 1 (lvl-1)*par1 hp adj ulvl totem lvl 30 % hitpoints more per level 5 % hitpoint bonus per level 250 duration of aura 0 ? 0 ? 0 ? 30 radius 2 radius/lvl 1 8 384 3000
Summon Spirit Wolf 227 dru summon spirit wolf 119 fireresist "min(ln78,85)" coldresist "min(ln78,85)" lightresist "min(ln78,85)" poisonresist "min(ln78,85)" item_armor_percent (par4 * lvl) + ln56 tohit ln12 damagepercent skill('Summon Grizzly'.ln12) spiritwolf spiritwolf "min(lvl,par3)" S1 druid_summon 1 0 none SC SC xx 1 4 1 6 20 Raven 1 1 8 15 0 1 1 skill('Summon Fenris'.ln12) hp % ulvl pet lvl 50 % feral add to attack 25 % feral attack per level 5 max wolves 7 defense points per level 50 % defense bonus 10 % defense bonus per level 0 resist 5 resist/lvl 1 75 20 8 2 1 2 4 5 8 6 1 2 4 5 8 384 3000
Wearbear 228 dru wearbear 116 bear 1000+skill('Shape Shifting'.ln12) damagepercent ln12 skill_armor_percent ln34 item_maxhp_percent par5 + skill('Shape Shifting'.ln34) bear_into bear_undo 45 1 0 none SC SC xx 22 1 6 20 1 25 1 1 8 15 0 1 55 % Base bonus damage 8 % bonus damage per level 25 % increase armorclass 6 % armor class per level 75 % Base bonus hitpoints 1 8 128 384 3000
Molten Boulder 229 dru molten boulder moltenboulderemerge par1 druid_firecast_b druid_fire_cast_1 moltenboulderemerge 1 0 none SC SC xx 1 1 druid_firecast_b druid_fire_cast_1 6 20 Firestorm 50 1 1 7 20 1 1 7 Explosion Radius 10 phys damage synergy 8 fire damage synergy 1 8 6 4 7 10 13 16 12 5 8 11 14 17 skill('Volcano'.blvl)*par7 fire 6 4 7 10 13 16 12 5 8 11 14 17 skill('Firestorm'.blvl)*par8 384 3000
Arctic Blast 230 dru arctic blast 11 19 arcticblast1 ln12/4-2 arctic_blast_cast druid_windcast 15 24 arcticblast1 arcticblast2 1 0 none SQ SQ xx 18 10 6 20 1 1 6 0 2 24 1 1 ln12/2 range 20 frames if monster 35 base ranged (doubled) 2 level range (doubled) 4 Min Mana to start casting 15 damage synergy 1 2 cold 21 16 18 20 24 29 40 16 19 21 25 31 (skill('Cyclone Armor'.blvl)+skill('Hurricane'.blvl))*par8 100 15 15 15 384 3000
Cycle of Life 231 dru cycle of life 115 vine_beast cycleoflife vine 1 S1 Vine Attack lvl CorpseCycler lvl druid_summon 1 0 none SC SC xx 1 4 12 20 Plague Poppy 1 1 8 10 0 1 1 par3 * (lvl-1) hp % ulvl * 3 / 4 + lvl vine lvl 10 Radius 0 Life steal moved --> 25 % more hitpoints per level 0 ? 3 min % life steal 12 max % life steal 1 8 512 3000
Feral Rage 232 dru feral rage 56 120 feralrage par1 velocitypercent dm34 lifedrainmindam par2 * lvl lifedrainmaxdam par2 * lvl 1 0 h2h A1 A1 xx 1 1 1 1 12 20 Wearwolf 2 wolf 1 1 8 3 0 1 ln56 dmg% lvl/par7 + par8 500 duration 4 % life steal per hit 10 Min Speed boost 70 Max Speed boost 50 % damage increase 5 % damage increase per level 2 Levels per charge 3 Min charge 1 20 10 8 128 512 3000
Maul 233 dru maul 56 120 maul par4 damagepercent lvl*par3 stunlength dm56 1 0 h2h A1 A1 xx 1 1 1 1 12 20 Wearbear 2 bear 1 1 8 3 0 1 0 dmg% lvl/par7 + par8 20 damage per charge 500 duration 10 min stun duration 100 max stun duration 2 Levels per charge 3 Min charge 1 20 10 8 128 512 3000
Eruption 234 dru eruption 28 erruption center druid_firecast_a druid_fire_cast_2 28 erruption center 1 0 none SC SC xx 1 4 1 1 druid_firecast_a druid_fire_cast_2 12 20 Molten Boulder 50 1 8 15 0 1 par1 radius par2 frequency 7 radius 6 Missile delay 0 Change duration in Missiles.xls 12 damage synergy 1 8 fire 15 6 12 16 18 22 25 6 12 16 19 23 (skill('Firestorm'.blvl)+skill('Volcano'.blvl))*par8 512 8000
Cyclone Armor 235 dru cyclone armor 18 cyclonearmor bonearmor (ln12*(100+(skill('Twister'.blvl)+skill('Tornado'.blvl)+skill('Hurricane'.blvl))*par8)/100)*256 bonearmormax (ln12*(100+(skill('Twister'.blvl)+skill('Tornado'.blvl)+skill('Hurricane'.blvl))*par8)/100)*256 absorbdamage 25 druid_cyclonearmor 1 0 none SC SC xx 1 1 12 20 Arctic Blast 1 8 5 1 1 1 40 damage absorbed 12 additional absorbed/level 7 absorb synergy 1 8 512 8000
Heart of Wolverine 236 dru heart of wolverine 119 poisonresist 100 maxpoisonresist 100 heartofwolverine totem 1 NU Wolverine Aura lvl druid_summon 1 0 none SC SC xx 1 4 18 20 Oak Sage 1 1 8 20 1 1 1 (lvl-1)*par1 hp adj ulvl totem lvl 25 bonus hitpoints per level 3 Bonus Level 25 % attack rating bonus 7 % attack rating bonus per lvl 20 % damage increase 7 % damage increase per level 30 radius 2 radius/lvl 1 8 640 8000
Summon Fenris 237 dru summon fenris 119 fireresist "min(ln78,85)" coldresist "min(ln78,85)" lightresist "min(ln78,85)" poisonresist "min(ln78,85)" item_armor_percent (par6 * lvl) + skill('Summon Spirit Wolf'.ln56) tohit skill('Summon Spirit Wolf'.ln12) damagepercent skill('Summon Grizzly'.ln12) fenris fenris "min(lvl,par3)" S1 druid_summon 1 0 none SC SC xx 1 4 1 18 20 Oak Sage Summon Spirit Wolf 1 1 8 20 0 1 1 ln12 hp% ulvl pet lvl 50 % feral hitpoints 25 % feral hitpoints per level 3 max fenri 500 rage duration - see fenris rage 100 % damage with rage - see fenris rage 4 defense points per level 0 resist 5 resist/lvl 1 150 20 8 7 2 3 6 8 11 12 2 3 6 9 13 640 8000
Rabies 238 dru rabies 57 121 rabiesplague rabies 1 0 h2h S3 S3 S3 1 1 1 1 18 20 Feral Rage 2 wolf 1 1 8 10 0 1 18 damage synergy 1 50 7 1 2 3 pois 6 4 5 7 11 16 14 4 5 7 11 16 (skill('Plague Poppy'.blvl))*par8 100 10 10 10 640 16000
Fire Claws 239 dru fire claws 58 2 fire_hit 1 0 h2h A1 A1 xx 1 1 1 1 18 20 Feral Rage Maul 2 wolf bear 1 1 8 4 0 1 0 dmg% 22 damage synergy 1 50 15 8 128 fire 15 8 12 20 24 30 20 8 12 22 26 34 (skill('Firestorm'.blvl)+skill('Molten Boulder'.blvl)+skill('Volcano'.blvl)+skill('Eruption'.blvl))*par8 640 16000
Twister 240 dru twister 118 twister druid_windcast 68 twister 1 0 none SC SC xx 1 1 18 20 Cyclone Armor 1 1 8 7 0 1 par1 # missiles 3 Number of twisters 10 Frames the target is stunned 10 damage synergy 1 7 12 4 7 10 13 16 16 4 7 11 14 17 (skill('Tornado'.blvl)+skill('Hurricane'.blvl))*par8 640 16000
Vines 241 dru vines 115 vine_beast vinecreature vine 1 S1 Vine Attack lvl VineCycler lvl druid_summon 1 0 none SC SC xx 1 4 24 20 Cycle of Life 1 1 8 14 1 1 1 par3 * (lvl-1) hp % ulvl * 3 / 4 + lvl vine lvl 3 Number of vines 1 vines per level 20 % more hitpoints per level 0 Mana steal moved --> 1 min % mana steal 8 max % mana steal 1 8 768 16000
Hunger 242 dru hunger 122 1 0 h2h S3 S3 S3 1 1 1 1 24 20 Fire Claws 2 wolf bear 1 1 8 3 0 1 par5 dmg% dm12 lifesteal dm34 manasteal 50 min % life steal 200 max % life steal 50 min % mana steal 200 max % mana steal -75 % damage penalty 1 50 10 8 128 768 32000
Shock Wave 243 dru shock wave 8 shockwave shockwave 17 shockwave shockwave 1 0 none SC SC xx 1 24 20 Maul 2 bear 1 1 8 7 0 1 5 # missiles 40 stun length 15 stun length per level 5 damage synergy 1 8 10 3 5 7 7 7 20 3 5 7 7 7 skill('Maul'.blvl)*par8 768 32000
Volcano 244 dru volcano 123 volcano par1 druid_firecast_a druid_fire_cast_2 91 volcano overlay fire volcano 1 0 none SC SC xx 1 4 1 1 sorceress_cast_fire druid_fire_cast_2 24 20 Eruption 100 1 8 25 0 1 par2 damage delay 12 debris range 2 delay between debris 12 damage synergy 12 fire damage synergy 1 8 8 2 4 6 8 10 10 2 4 6 8 10 skill('Molten Boulder'.blvl)*par7 fire 8 2 4 6 8 11 10 2 4 6 8 13 (skill('Eruption'.blvl)+skill('Armageddon'.blvl))*par8 768 32000
Tornado 245 dru tornado 118 tornado 42371 par2 druid_windcast 69 tornado 1 0 none SC SC xx 1 4 1 24 20 Twister 1 1 8 10 0 1 1 # missiles par1 damage delay 15 Frame delay between damage 3 Radius of damage 9 damage synergy 1 8 25 8 14 20 24 28 35 8 15 21 25 29 (skill('Cyclone Armor'.blvl)+skill('Twister'.blvl)+skill('Hurricane'.blvl))*par8 768 32000
Spirit of Barbs 246 dru spirit of barbs 119 poisonresist 100 maxpoisonresist 100 spiritofbarbs totem 1 NU Barbs Aura lvl druid_summon 1 0 none SC SC xx 1 4 30 20 Heart of Wolverine 1 1 8 25 1 1 1 (lvl-1)*par1 hp adj ulvl totem lvl 25 % hitpoints more per level 0 Bonus Level 50 % thorns damage back 20 % thorns damage back per level 30 radius 2 radius/lvl 1 8 896 64000
Summon Grizzly 247 dru summon grizzly 119 fireresist "min(ln78,85)" coldresist "min(ln78,85)" lightresist "min(ln78,85)" poisonresist "min(ln78,85)" damagepercent ln12 tohit skill('Summon Spirit Wolf'.ln12) item_armor_percent skill('Summon Spirit Wolf'.ln56) druidbear grizzly 1 S1 druid_summon 1 0 none SC SC xx 1 4 30 20 Summon Fenris 1 1 8 40 0 1 1 skill('Summon Fenris'.ln12) hp % ulvl pet lvl 25 % feral add to damage 10 % feral damage per level 0 resist 5 resist/lvl 1 300 20 8 30 10 15 20 26 30 60 10 15 20 26 30 896 64000
Fury 248 dru fury 37 13 53 21 1 0 h2h A1 A1 A1 1 1 1 30 20 Rabies 2 wolf 1 1 8 4 0 1 "min((par5 + lvl -1), par6)" max targets ln34 damage % 100 % frame rollback 100 % damage percent 17 damage % per level 2 Targets 5 Max targets 1 50 7 8 128 896 64000
Armageddon 249 dru armageddon 124 armageddoncontrol armageddoncontrol armageddoncontrol armageddon ln12 + skill('Eruption'.blvl) * par7 par3 druid_firecast_a druid_fire_cast_2 92 armageddontail armageddonrock 25 frames 25 fall rate 15 slide rate 1 0 none SC SC xx 1 4 30 20 Volcano Hurricane 1 150 1 8 35 0 1 250 base duration 0 duration per level 8 radius to drop meteors 8 frame delay between rocks 50 duration synergy 14 damage synergy 1 8 4 4 6 8 10 12 16 4 7 9 12 14 fire 25 15 20 25 31 38 75 16 22 27 34 40 (skill('Firestorm'.blvl)+skill('Molten Boulder'.blvl)+skill('Volcano'.blvl))*par8 896 64000
Hurricane 250 dru hurricane 124 42883 hurricane ln12 + skill('Cyclone Armor'.blvl) * par7 par3 druid_windcast druid_hurricane hurricaneswoosh hurricanerock hurricanetree 3 debris/frame 75 height 1 0 none SC SC xx 1 30 20 Tornado 150 1 8 30 0 1 250 base duration 0 duration per level 9 radius 20 frames per hit search 50 duration synergy 9 damage synergy 1 1 2 8 cold 25 7 10 12 14 16 50 7 10 12 14 16 (skill('Twister'.blvl)+skill('Tornado'.blvl))*par8 50 896 64000
Fire Trauma 251 ass fire trauma bomb in air 1 par1 weapon_throw_1 bomb in air 1 0 rng S2 S2 S2 1 1 20 1 1 5 24 1 1 5 radius 9 damage synergy 1 7 fire 6 3 8 20 38 58 8 5 11 24 44 66 (skill('Shock Field'.blvl) + skill('Death Sentry'.blvl) + skill('Charged Bolt Sentry'.blvl) + skill('Lightning Sentry'.blvl) + skill('Wake of Fire Sentry'.blvl) + skill('Inferno Sentry'.blvl)) * par8 5 256 1000
Claw Mastery 252 ass claw mastery clawmastery h2h passive_mastery_melee_th ln12 passive_mastery_melee_dmg ln34 passive_mastery_melee_crit dm56 1 0 none h2h SC SC SC 1 1 20 0 8 0 0 1 1 30 Attack rating bonus percent 10 Percent attack rating per level 35 % damage percent 4 damage % per level 0 critical% 25 critical% 1 8 10 256 1000
Psychic Hammer 253 ass psychic hammer 22 33 1 assassin_psychichammer psychic_hammer_hit paladin_holybolt_impact_1 psychic_hammer_curse 5 3 1 0 rng SC SC SC 1 4 1 1 1 20 1 6 16 1 1 par1 knockback% vs. monster dm34 knockback% vs. unique dm56 knockback% vs. boss dm56 knockback% vs. player 100 knockback % vs. monster 50 min knockback% vs. unique 100 max knockback% vs. unique 25 min knockback% vs. boss 99 max knockback% vs. boss 1 7 2 2 3 4 5 6 6 3 4 5 6 7 mag 2 2 3 4 5 6 6 3 4 5 6 7 5 256 1000
Tiger Strike 254 ass tiger strike 23 34 1 progressive_damage par3 progressive_damage progressive_tohit par4 assassinfootimpact tigerstrike1 assassin_chargeup_tiger_1 12 12 12 1 0 h2h mele A1 A1 A2 1 1 1 1 1 20 1 1 8 1 0 1 1 ln12 progressive damage 100 % Damage bonus 20 % damage per level 375 duration 50 tohit bonus for each charge-up 1 15 7 8 128 4 256 1000
Dragon Talon 255 ass dragon talon 24 42 assassin_kick_1 6 4 1 0 h2h 4 KK KK A1 19 1 1 1 1 20 1 1 8 6 0 1 1 lvl/6+1 number of kicks dm34 knockback% vs. unique dm56 knockback% vs. boss dm56 knockback% vs. player 5 Percent damage 7 percent damage per level 50 min knockback% vs. unique 100 max knockback% vs. unique 25 min knockback% vs. boss 99 max knockback% vs. boss 1 20 35 1 8 4 256 1000
Shock Field 256 ass shock field 43 par1+lvl/par2+skill('Fire Trauma'.blvl)/3 shock field in air (par1+lvl/par2+skill('Fire Trauma'.blvl)/3)/4+1 weapon_throw_1 5 shock field in air 1 0 rng S2 S2 S2 1 6 20 Fire Trauma 15 1 1 8 6 0 1 6 Number of missiles 4 Levels per missile 11 damage synergy 1 7 ltng 2 0 0 0 0 0 20 6 12 20 30 42 (skill('Charged Bolt Sentry'.blvl) + skill('Lightning Sentry'.blvl) + skill('Death Sentry'.blvl)) * par8 11 384 3000
Blade Sentinel 257 ass blade sentinel 44 blade creeper tohit lvl*5 bladecreeper assassintrap 5 S1 Blade Sentinel lvl assassin_summon 1 0 rng S2 S2 S2 1 6 20 50 1 1 8 7 0 1 ln12 duration 100 duration 12 duration per level 5 Total number assassin traps 3 Total number pet traps 1 8 48 6 3 4 5 5 5 10 3 4 5 5 5 5 3 384 3000
Quickness 258 ass quickness 18 quickness ln56 velocitypercent dm12 attackrate dm34 assassin_quickness 1 0 none SC SC SC 1 6 20 Claw Mastery 1 8 10 0 1 1 15 % min increased walk speed 70 % max increased walk speed 15 min % increased attack speed 60 max % increased attack speed 3000 duration 300 duration per level 1 8 1 384 3000
Fists of Fire 259 ass fists of fire 23 35 1 143 38 39 par1 par2 4 fistsoffirefirewall progressive_fire par3 progressive_fire progressive_tohit par4 9 9 fistsoffireexplode fistsoffirefirewall 1 0 h2h 3 h2h SQ A1 xx 16 1 1 1 1 6 20 1 1 8 2 0 1 1 lvl*3 convert to fire% 4 Radius of explosion 4 Radius of firewall fragments 375 duration 50 tohit bonus for each charge-up 12 damage synergy 1 15 7 8 128 fire 6 5 10 20 30 40 10 5 11 22 33 44 (skill('Royal Strike'.blvl)) * par8 4 384 3000
Dragon Claw 260 ass dragon claw 25 46 assassin_kick_1 1 0 h2h 3 h2h h2h SQ A1 xx 16 1 1 1 1 6 20 Dragon Talon 1 1 8 2 0 1 1 ln12 + skill('Claw Mastery'.blvl)*par8 damage% 50 % Damage bonus 5 % damage per level 4 damage synergy 1 40 25 8 128 4 384 3000
Charged Bolt Sentry 261 ass charged bolt sentry 45 chargeboltsentry assassintrap 5 S1 BoltSentry lvl Fire Trauma skill('Fire Trauma'.blvl) Shock Field skill('Shock Field'.blvl) Lightning Sentry skill('Lightning Sentry'.blvl) Death Sentry skill('Death Sentry'.blvl) assassin_summon 1 0 rng S2 S2 S2 1 4 12 20 Shock Field 1 8 13 0 1 par1 + skill('Lightning Sentry'.blvl)/4 shots fired 5 Shots fired 5 Bolts to send out 0 Bolts to send out per level 6 damage synergy 1 7 ltng 2 0 0 0 0 0 14 6 8 12 14 16 (skill('Fire Trauma'.blvl) + skill('Lightning Sentry'.blvl) + skill('Death Sentry'.blvl)) * par8 3 512 3000
Wake of Fire Sentry 262 ass wake of fire sentry 45 wakeofdestruction assassintrap 5 S1 Wake Of Destruction Sentry lvl Fire Trauma skill('Fire Trauma'.blvl) Inferno Sentry skill('Inferno Sentry'.blvl) 32 assassin_summon 1 0 rng S2 S2 S2 1 4 12 20 Fire Trauma 1 8 13 0 1 5 Shots fired 8 damage synergy 1 8 fire 5 2 3 5 7 9 10 2 3 6 8 10 (skill('Fire Trauma'.blvl) + skill('Inferno Sentry'.blvl)) * par8 3 512 3000
Weapon Block 263 ass weapon block weaponblock h2h passive_weaponblock dm12 1 0 rng h2h h2h SC SC SC 1 12 20 Claw Mastery 1 0 8 0 0 1 1 20 min block % 65 max block % 1 8 10 512 3000
Cloak of Shadows 264 ass cloak of shadows 47 57347 cloak_of_shadows cloaked ln34 dm12 skill_armor_percent "-min(ln56,95)" item_armor_percent ln78 7 1 0 none SC SC SC 1 12 20 Psychic Hammer 1 8 13 0 1 30 min radius of effect 30 max radius of effect 200 duration 25 Duration per level 15 percent armor decrease 3 percent decrease per level 10 percent armor bonus 3 percent armor bonus per level 1 8 2 512 8000
Cobra Strike 265 ass cobra strike 23 34 2 progressive_steal par3 progressive_steal progressive_tohit par4 cobrastrike1 assassin_chargeup_cobra_1 13 13 13 1 0 h2h mele A1 A1 A2 1 1 1 1 12 20 Tiger Strike 1 1 8 2 0 1 1 40 Base percent life steal 5 Percent life steal per level 375 duration 50 tohit bonus for each charge-up 1 15 7 8 128 12 512 8000
Blade Fury 266 ass blade fury 26 48 par4 bladefragment1 8 6 bladefragment2 1 0 rng SQ A1 SC 23 12 18 20 Blade Sentinel Wake of Fire Sentry 1 1 1 6 0 5 8 1 1 0 "Don't Use, missile range mod" 0 "Don't Use, missile range mod" 3 Min Mana to start casting 5 Frame delay between blades 1 8 96 8 3 5 8 8 8 10 3 5 8 8 8 5 640 8000
Fade 267 ass fade 18 fade ln56 fireresist dm12 coldresist dm12 lightresist dm12 poisonresist dm12 curse_resistance dm34 damageresist ln78 fade 2 assassin_fade 1 0 none SC SC SC 1 1 18 20 Quickness 1 8 10 0 1 1 10 min Elemental resistance 75 max elemental resist 40 min curse length reduction 90 max curse length reduction 3000 duration 300 Duration per level 1 damage resist % 1 damage resist % per level 1 8 1 640 8000
Shadow Warrior 268 ass shadow warrior 49 shadowwarrior tohit lvl*par2 skill_armor_percent lvl*par3 strength lvl*10 dexterity lvl*10 fireresist "min(lvl*4,75)" coldresist "min(lvl*4,75)" lightresist "min(lvl*4,75)" poisonresist "min(lvl*4,75)" shadowwarrior shadowwarrior 1 NU 42 assassin_summon 1 0 none SC SC SC 1 4 18 20 Cloak of Shadows Weapon Block 150 1 7 54 1 1 1 15 Plus % HP per level 40 plus to hit per level 12 Plus % AC per level 0 ? 18 base item quality level 2 item quality per level 1 8 3 640 16000
Claws of Thunder 269 ass claws of thunder 23 35 1 36 37 par1 4 clawsofthundernova clawsofthunderbolt progressive_lightning par3 progressive_lightning progressive_tohit par4 10 11 clawsofthundernova clawsofthunderbolt 1 0 h2h 3 h2h SQ A1 xx 16 1 1 1 18 20 Fists of Fire 1 1 8 4 0 1 1 4 skip 375 duration 50 tohit bonus for each charge-up 8 damage synergy 1 15 7 8 128 ltng 1 0 0 0 0 0 80 20 40 60 80 100 (skill('Royal Strike'.blvl)) * par8 4 640 16000
Dragon Tail 270 ass dragon tail 27 50 dragontail missile par3 assassin_kick_1 9 7 dragontail missile 1 0 h2h 4 KK KK A1 1 1 1 18 20 Dragon Claw 1 1 8 10 0 1 1 ln12 damage% 50 Percent area damage min 10 percent area damage per level 6 Radius -40 Attack rate penalty 1 20 15 1 8 fire 4 640 16000
Lightning Sentry 271 ass lightning sentry 45 lightningsentry assassintrap 5 S1 sentry lightning lvl Shock Field skill('Shock Field'.blvl) Charged Bolt Sentry skill('Charged Bolt Sentry'.blvl) Death Sentry skill('Death Sentry'.blvl) assassin_summon 1 0 rng S2 S2 S2 1 4 24 20 Charged Bolt Sentry 1 8 20 0 1 10 Shots fired 12 damage synergy 1 8 ltng 1 0 0 0 0 0 20 10 16 24 34 44 (skill('Shock Field'.blvl) + skill('Charged Bolt Sentry'.blvl) + skill('Death Sentry'.blvl))*par8 3 768 16000
Inferno Sentry 272 ass inferno sentry 45 infernosentry assassintrap 5 S1 mon inferno sentry lvl Fire Trauma skill('Fire Trauma'.blvl) Wake of Fire Sentry skill('Wake of Fire Sentry'.blvl) Death Sentry skill('Death Sentry'.blvl) 32 assassin_summon 1 0 rng S2 S2 S2 1 4 24 20 Wake of Fire Sentry 1 8 20 0 1 10 Shots fired 10 synergy damage bonus 7 damage synergy 1 4 fire 20 17 21 26 32 39 50 19 23 28 34 41 (skill('Fire Trauma'.blvl) + skill('Death Sentry'.blvl)) * par7 + skill('Wake of Fire Sentry'.blvl)*par8 3 768 32000
Mind Blast 273 ass mind blast 51 33667 par7 assassin_psychichammer fist_will_cast 8 mindblast center mindblast hit 1 0 rng SC SC SC 1 1 1 24 20 Cloak of Shadows 1 8 15 0 1 50 length of stun 5 additional lengh of stun 150 conversion length min 100 conversion length random range 15 chance for conversion 40 max chance for conversion 4 radius 1 9 8 10 2 5 8 8 8 20 2 5 8 8 8 stun 50 5 5 5 11 768 32000
Blades of Ice 274 ass blades of ice 23 35 1 38 39 par1 par2 4 bladesoficecubes 34571 progressive_cold par3 progressive_cold progressive_tohit par4 9 9 bladesoficeexplode bladesoficecubes 1 0 h2h 3 h2h SQ A1 xx 16 1 1 1 1 24 20 Claws of Thunder 1 1 8 3 0 1 1 6 Second level radius 3 Third level radius 375 duration 50 tohit bonus for each charge-up 1 freeze length divisor 8 damage synergy 1 15 7 8 128 cold 15 8 10 20 30 40 35 8 10 22 32 42 (skill('Royal Strike'.blvl)) * par8 100 10 10 10 4 768 32000
Dragon Flight 275 ass dragon flight 12 52 par7 1 sorceress_teleport dragonflight 5 1 1 0 rng 4 SQ A1 xx 21 1 1 24 20 Dragon Tail 25 1 1 8 15 0 1 1 100 % Damage bonus 25 % damage per level 27 range 1 60 25 1 8 13 768 32000
Death Sentry 276 ass death sentry 45 deathsentry assassintrap 5 S1 mon death sentry lvl death sentry ltng lvl Fire Trauma skill('Fire Trauma'.blvl) Lightning Sentry skill('Lightning Sentry'.blvl) 32 assassin_summon 1 0 rng S2 S2 S2 1 4 30 20 Lightning Sentry 1 8 20 0 1 5 Shots fired 12 damage synergy 1 8 ltng 1 0 0 0 0 0 50 8 14 22 28 34 (skill('Lightning Sentry'.blvl))*par8 3 896 64000
Blade Shield 277 ass blade shield 28 54 blade shield attachment 33667 bladeshield ln12 par4 assassin_bladeshield 1 0 none SC SC SC 1 30 20 Blade Fury 1 8 27 2 1 1 1 par3 500 duration 125 duration per level 25 delay 6 radius to attack in 1 32 3 8 32 1 5 6 7 7 7 30 5 6 7 7 7 1 896 64000
Venom 278 ass venom 18 venomclaws ln12 poisonmindam enms poisonmaxdam exms skill_poison_override_length edma assassin_venom 1 0 none SC SC SC 1 1 30 20 Fade 1 8 12 0 1 1 3000 duration 100 duration per level 0 ? 0 ? 0 ? 0 ? 1 6 pois 24 6 8 10 12 14 32 6 8 10 12 14 10 1 896 64000
Shadow Master 279 ass shadow master 49 shadowwarrior tohit lvl*par2 strength lvl*10 dexterity lvl*10 fireresist dm34 coldresist dm34 lightresist dm34 poisonresist dm34 shadowmaster shadowwarrior 1 NU 42 assassin_summon 1 0 none SC SC SC 1 30 20 Shadow Warrior 150 1 7 70 1 1 1 15 Pluse % HP per level 40 plus to hit per level 5 Min % resist all 90 Max % resist all 24 base item quality level 3 item quality per level 1 8 3 896 64000
Royal Strike 280 ass royal strike 23 34 40 143 41 par6 par5 royalstrikemeteorcenter royalstrikechainlightning royalstrikechaosice progressive_other 375 par1 progressive_other progressive_tohit par7 14 83 15 royalstrikemeteorcenter royalstrikechainlightning royalstrikechaosice 1 0 h2h mele A1 A1 A2 1 1 1 1 30 20 Cobra Strike Blades of Ice 1 1 8 4 0 1 1 par2 8 radius of jump to next target 6 radius of meteor explosion 30 Frames of fire 15 Frames of fire per level 16 chaos ice bolts 10 chain lightning skip 25 to hit bonus per charge up 1 15 7 8 128 4 -3 896 64000
Wake Of Destruction Sentry 281 125 wake of destruction maker 70 wake of destruction maker 1 0 none SQ SQ S2 1 1 1 par8 shots fired 6 Number of missiles 2 Number of missiles per level 2 Min Range 5 Shots Fired 1 8 0
Imp Inferno 282 59 126 impinfernoflame1 71 impinfernoflame1 impinfernoflame2 8 z offset lvl-1 range 1 0 none SQ SQ SC 6 1 1 1 "rand(par3,par4)" len lvl-1 range 30 base ranged (doubled) 3 level range (doubled) 100 min frames 120 max frames 1 4 fire 18 15 15 15 15 15 37 15 15 15 15 15 0
Imp Fireball 283 impfireball 1 sorceress_cast_fire fire_cast_2 72 impfireball 40 z offset 1 0 none SQ SQ S2 1 1 1 1 8 0
Baal Taunt 284 28 baal taunt control monster_baal_taunt_1 46 baal taunt control 1 0 none SQ SQ A1 1 1 1 3 45 45 Delay in poison clouds 3 Delay in lightning 1 8 0
Baal Corpse Explode 285 141 ln34 82 1 0 none SQ SQ S3 1 1 1 1 % of base monster HP min damage 1 % of base monster HP max damage 6 radius (half squares) for damage 1 additional radius/level (half squares) for damage 40 Radius of search for corpses 1 8 0
Baal Monster Spawn 286 baal spawn monsters 1 baal_summon baal spawn monsters 1 0 none SQ SQ S3 1 1 1 1 8 0
Catapult Charged Ball 287 28 catapultchargedball 47 catapultchargedball 16 fall rate 1 0 none SQ SQ A1 1 1 1 4 Charged bolts per level 1 8 0
Catapult Spike Ball 288 28 catapult spike ball 47 catapult spike ball 16 fall rate 1 0 none SQ SQ A1 1 1 1 ln12 20 Spikes to launch 5 Spikes per level 1 8 0
Suck Blood 289 60 127 1 0 none SQ SQ A1 1 1 1 par1 heal% 25 Percent life to boss 1 8 1 3 0
Cry Help 290 128 curseattract 1 0 none SQ SQ S1 1 1 ln12 duration 100 Time for minions to attack target 20 Time per level 1 8 0
Healing Vortex 291 healing vortex healing vortex 1 0 none SQ SQ S2 1 1 1 1 8 10 5 5 5 5 5 20 5 5 5 5 5 0
Teleport 2 292 98 48 73 1 0 none SQ SQ S1 1 1 1 1 8 0
Self-resurrect 293 61 38 1 0 none SQ SQ S1 1 1 1 1 8 0
Vine Attack 294 130 plague vines slowed velocitypercent -100 druidpod_attack_1 druidpod_walk1_1 druidpod_neutral_1 49 vine beast attack vines vine beast walk 1 vine beast neutral "min(12,ln12)" #vines 35 delay 6 min dist between missiles 1 0 none SQ SQ S1 1 1 1 "min(24,ln12)" # missiles 20 frames to apply aura to target 3 Number of vines 1 vines per level 1 8 0
Overseer Whip 295 131 bloodlust par2 velocitypercent par3 attackrate par4 skill_armor_percent par5 damagepercent par6 suicideminion1 S1 33 1 0 none SQ SQ A2 1 1 1 par1 chance 65 Chance Bloodlust 250 Bloodlust duration 80 Velocity change 80 Speed change 50 Armor change 100 Damage change 1 8 0
Barbs Aura 296 65 65795 barbscontrol barbs ln12 thorns_percent ln34 1 0 none SQ SQ A1 1 1 1 1 30 Radius 2 radius per level 50 % thorns damage back 10 % thorns damage back per level 1 8 0
Wolverine Aura 297 65 65795 wolverinecontrol wolverine ln12 item_tohit_percent ln34 damagepercent ln56 1 0 none SQ SQ A1 1 1 1 1 30 Radius 2 radius per level 25 % attack rating bonus 7 % attack rating bonus per lvl 20 % damage increase 7 % damage increase per level 1 8 0
Oak Sage Aura 298 65 65795 oaksagecontrol oaksage ln12 item_maxhp_percent ln34 1 0 none SQ SQ A1 1 1 1 1 30 Radius 2 radius per level 30 % hitpoints more per level 5 % hitpoint bonus per level 1 8 0
Imp Fire Missile 299 132 impmiss21 sorceress_cast_fire fist_will_cast impmiss21 1 0 none A1 A1 A1 1 1 lvl-1 range adder ? ? ? ? ? ? 1 8 0
Impregnate 300 133 painworm1 NU 1 0 none S1 S1 S1 1 1 1 ? ? ? ? ? ? 1 8 0
Siege Beast Stomp 301 134 par5 siege_beast_dust 75 0 none A2 A2 A2 1 1 1 8 screen shake magnitude 5 screen shake bulid 20 screen shake duration 15 screen shake fade 25 radius of effect ? 1 1 8 20 10 10 10 10 10 60 10 10 10 10 10 0
MinionSpawner 302 62 135 spawnedminion 1 0 none A1 A1 A1 1 1 ? ? ? ? ? ? 1 8 0
CatapultBlizzard 303 28 catapult cold ball 47 catapult cold ball 16 fall rate 1 0 none A1 A1 A1 1 1 ? ? ? ? ? ? 1 8 0
CatapultPlague 304 28 catapult plague ball 47 catapult plague ball 16 fall rate 1 0 none A1 A1 A1 1 1 ? ? ? ? ? ? 1 8 0
CatapultMeteor 305 28 catapult meteor ball par1 47 catapult meteor ball 16 fall rate 1 0 none A1 A1 A1 1 1 5 radius of explosion ? 50 duration base 3 duration/level ? ? 1 8 0
BoltSentry 306 17 sentrychargedbolt 23 sentrychargedbolt 1 0 none SC SC xx 1 1 ln12 + skill('Shock Field'.blvl)/3 # bolts par8 + skill('Lightning Sentry'.blvl)/4 shots fired 5 Bolts to send out 0 Bolts to send out per level 5 Shots Fired 1 8 0
CorpseCycler 307 63 recycler delay 10 49 93 vine beast attack recycler delay 1 0 none SQ SQ S1 1 1 1 1 dm12 life steal 3 min % life steal 12 max % life steal 1 8 0
DeathMaul 308 136 death mauler 76 death mauler death mauler trail 1 0 none A1 A1 xx 1 1 96 missile animrate ? ? ? ? ? ? 1 8 10 4 4 4 4 4 20 4 4 4 4 4 0
Defense Curse 309 30 3 defense_curse ln34 ln12 skill_armor_percent -ln56 18 30 curseeffectred cursecast 1 0 none SC SC S2 1 1 3 radius 1 radius per level 200 duration 75 additional duration/level 50 % defense 5 % defense/lvl 1 0
Blood Mana 310 30 3 blood_mana ln34 ln12 18 30 curseeffectred cursecast 1 0 none SC SC S2 1 1 3 radius 1 radius per level 200 duration 75 additional duration/level 40 Max hitpoints to remove curse 1 8 10 4 4 4 4 4 20 4 4 4 4 4 0
mon inferno sentry 311 53 95 inferno sentry 1 52 77 inferno sentry 1 inferno sentry 2 -37 z offset 1 0 none SQ SQ xx 1 1 ln34/2 + skill('Wake of Fire Sentry'.blvl) range par1 + skill('Wake of Fire Sentry'.blvl) length of fire 3 density par8 shots fired 15 frame length 40 range times two 10 Shots fired 1 8 0
mon death sentry 312 55 ln34 necromancer_corpseexp_1 death_sentry 50 78 corpseexplosion deathsentryexplode 1 0 none SQ SQ xx 1 1 1 par1 damage % par2 damage % 50 fire % par8 + skill('Fire Trauma'.blvl)/3 shots fired 40 % of base monster HP min damage 80 % of base monster HP max damage 10 radius (half squares) 1 additional radius/level (half squares) 5 Shots Fired 1 8 fire 0
sentry lightning 313 sentrylightningbolt sentrylightningbolt 1 0 none SQ SQ xx 1 1 1 par8 shots fired 10 minimum damage 20 max damage 4 increase in dam/level (min & max) 10 Shots Fired 1 8 0
fenris rage 314 137 fenris_rage par1 damagepercent par2 necromancer_corpseexp_1 51 79 corpseexplosion 1 0 none A1 A1 A1 1 1 1 1 500 rage duration - see fenris rage 100 % damage with rage - see fenris rage 1 8 0
Baal Tentacle 315 140 1 0 none S2 S2 S2 1 1 1 ? ? ? ? 1 0
Baal Nova 316 22 baal nova baal_novacast fire_cast_2 25 baal nova 1 0 none S3 S3 S3 1 1 1 8 fire 50 24 32 32 32 32 75 24 32 32 32 32 0
Baal Inferno 317 53 95 baal inferno baal_missilecast 54 baal inferno 1 0 none SQ SQ xx 1 1 par1 length of fire 2 density 20 frame length ? 1 5 mag 64 48 48 48 48 48 96 48 48 48 48 48 0
Baal Cold Missiles 318 139 baal cold maker baal_coldtrailcast 81 baal cold maker 1 0 none A1 A1 A1 1 1 1 1 8 cold 20 9 13 13 13 13 40 9 13 13 13 13 200 50 50 50 0
MegademonInferno 319 53 95 megademoninferno 54 fetishinferno1 fetishinferno2 1 0 none SQ SQ S1 1 1 par1 length of fire 2 density 15 frame length 1 8 0
EvilHutSpawner 320 49 91 spawnedflames 36 50 1 0 none SQ SQ xx 1 1 1 8 0
CountessFirewall 321 24 countessfirewallmaker countessfirewall sorceress_cast_fire 26 countessfirewallmaker 1 0 none SQ SQ A1 1 1 1 0
ImpBolt 322 17 imp charged bolt 23 imp charged bolt 1 0 none SQ SQ A1 1 1 lvl+2 #bolts 1 8 0
Horror Arctic Blast 323 53 95 frozenhorror arcticblast1 54 frozenhorror arcticblast1 frozenhorror arcticblast1 1 0 none SQ SQ xx 18 1 1 par1 length of fire 2 density 15 frame length 1 8 0
death sentry ltng 324 sentrylightningbolt2 sentrylightningbolt2 1 0 none SQ SQ xx 1 1 1 5 Shots Fired 1 8 0
VineCycler 325 63 vine recycler delay 10 49 93 vine beast attack recycler delay 1 0 none SQ SQ S1 1 1 1 1 dm12 mana steal 1 min % mana steal 8 max % mana steal 1 8 0
BearSmite 326 32 2 druidbear_attack_1 1 9 h2h S1 S1 xx 1 1 1 1 ln34 damage % "max(250,ln12)" stunlen 15 Stun Length 5 additional frames/level 15 Percent bonus damage 15 percent damage per level 1 8 8 128 0
Resurrect2 327 97 healing 39 1 0 none SQ SQ xx 1 1 1 1 1 1 1 1 0
BloodLordFrenzy 328 37 109 monfrenzy ln12 velocitypercent dm34 attackrate dm34 other_animrate dm34 weapon_giant_1 1 weapon_giant_1 1 3 none SQ SQ A2 1 1 1 1 1 200 duration 25 duration/level 30 min % speed increase 110 max % speed increase 1 10 10 8 128 0
Baal Teleport 329 98 48 73 baalteleport 1 0 none SQ SQ S1 1 1 1 1 8 0
Imp Teleport 330 129 attached sorceress_teleport 48 74 imp teleport imp teleport 1 0 none SQ SQ S1 1 1 1 1 8 0
Baal Clone Teleport 331 98 48 73 baalclonedeath baalteleport 1 0 none SQ SQ S1 1 1 1 1 8 0
ZakarumLightning 332 monsterlight sorceress_cast_lightning light_cast_1 monsterlight 1 0 none SQ SQ S1 1 1 1 8 0
VampireMissile 333 firehead firehead 1 0 none SC SC SC 1 1 1 8 0
MephistoMissile 334 mephisto mephisto 1 0 none SC SC A2 1 1 1 8 0
DoomKnightMissile 335 148 undeadmissile1 94 undeadmissile1 1 0 none SC SC S1 1 1 1 8 0
RogueMissile 336 110 rogue1 66 rogue1 1 0 none A1 A1 A1 1 1 1 8 0
HydraMissile 337 hydra hydra 1 0 none SC SC SC 1 1 1 8 0
NecromageMissile 338 149 necromage1 95 necromage1 1 0 none SC SC A1 1 1 1 8 0
MonBow 339 4 cr_arrow6 11 cr_arrow6 1 0 rng A1 A1 A1 1 1 1 1 8 128 0
MonFireArrow 340 4 firearrow 11 firearrow 1 0 rng A1 A1 A1 1 1 1 1 10 10 8 128 fire 1 8 8 8 8 8 4 9 9 9 9 9 0
MonColdArrow 341 4 coldarrow 11 coldarrow 1 4 rng A1 A1 A1 1 1 1 1 10 10 8 128 cold 1 7 7 7 7 7 4 8 8 8 8 8 100 25 25 25 0
MonExplodingArrow 342 4 explodingarrow 11 explodingarrow 1 5 rng A1 A1 A1 1 1 1 1 10 10 8 128 fire 2 8 8 8 8 8 6 9 9 9 9 9 0
MonFreezingArrow 343 4 freezingarrow 11 freezingarrow 1 7 rng A1 A1 A1 1 1 1 par1 damage radius 5 Radius of impact 1 10 10 8 128 cold 2 7 7 7 7 7 6 8 8 8 8 8 100 25 25 25 0
MonPowerStrike 344 6 2 1 4 h2h A1 A1 A1 1 1 1 1 1 1 1 20 15 8 128 ltng 1 0 0 0 0 0 16 12 12 12 12 12 0
SuccubusBolt 345 4 succubusmiss 11 succubusmiss 1 0 rng SC SC S2 1 1 1 1 8 mag 10 2 4 6 6 6 15 2 5 7 7 7 0
MephFrostNova 346 22 mephfrostnova mephfrostnova mephfrostnova sorceress_cast_cold 25 frostnova 1 6 none SC SC A2 1 1 1 9 radius of freeze 3 additional radius/level 1 8 cold 40 20 20 20 20 20 60 20 20 20 20 20 200 50 50 50 0
MonIceSpear 347 6 2 1 4 h2h A1 A1 A1 1 1 1 1 1 1 1 20 15 8 128 cold 10 8 8 8 8 8 14 9 9 9 9 9 125 25 25 25 0
ShamanIce 348 glacialspike ln34 * (100 + skill('Blizzard'.blvl) * par7) / 100 ln12 sorceress_cast_cold glacialspike 1 7 none SC SC xx 1 1 1 4 radius 0 radius per level 50 freeze frames 3 freeze frames per level 1 8 cold 4 10 14 14 14 14 12 10 15 15 15 15 125 50 50 50 0
Diablogeddon 349 124 diablogeddoncontrol diablogeddoncontrol diablogeddoncontrol armageddon ln12 par3 druid_firecast_a 92 diablogeddontail diablogeddonrock 25 frames 25 fall rate 15 slide rate 1 0 none SC SC S3 1 1 1 1000 base duration 50 duration per level 8 radius to drop meteors 8 frame delay between rocks 1 8 4 4 6 8 10 12 16 4 7 9 12 14 fire 25 15 20 25 30 35 75 16 22 27 33 38 0
Delerium Change 350 delerium change 116 delerium 1500 velocitypercent 33 attackrate 33 other_animrate 33 45 1 0 none SC SC NU 1 1 1 1 8 0
NihlathakCorpseExplosion 351 17 55 "min(30,16+lvl)" 1 necromancer_corpse_cast necromancer_corpseexp_1 21 32 corpseexplosion explodingarrowexp redlightmissile 1 0 none SC SC xx 1 1 1 1 1 1 par1 % target hp min damage par2 % target hp max damage par5 % damage to do as elemental 10 % of base monster HP min damage 20 % of base monster HP max damage 50 % damage to do as elemental 1 8 fire 0
SerpentCharge 352 31 67 25 37 1 2 none SQ A1 xx 4 1 1 1 1 150 percent increase in velocity 0 plus % damage 0 plus % dam per level 1 50 15 8 128 0
Trap Nova 353 22 trapnova trapnova trapnova sorceress_cast_lightning light_cast_1 25 trapnova 1 2 none SC SC xx sorceress_cast_lightning 1 12 number of missiles 4 additional missiles per level 1 8 0
UnHolyBoltEx 354 unholybolt1 unholybolt1 1 0 none SQ SQ xx 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
ShamanFireEx 355 shafire1 shafire1 1 0 none SQ SQ xx 1 1 0 ? 0 ? 0 ? 0 ? 0 ? 0 ? 1 8 0
Imp Fire Missile Ex 356 impmiss21 sorceress_cast_fire fist_will_cast impmiss21 1 0 none A1 A1 A1 1 1 lvl-1 range adder ? ? ? ? ? ? 1 8 0

328
scripts/pack-overlays.ts Normal file
View File

@ -0,0 +1,328 @@
/**
* Offline Cast Overlay Asset & Data Baking Script.
*
* Extracts authentic Diablo II v1.13c cast overlay DCC sprites from d2data.mpq:
* - FireCast_for_Sorceress.dcc (fire_cast_1, 14 frames, box: [-55, -127, 117, 164])
* - FireCast2.dcc (fire_cast_2, 16 frames, box: [-74, -89, 145, 133])
* - IceCastNew01.dcc (ice_cast_1, 15 frames, box: [-49, -90, 97, 55])
* - IceCastNew02.dcc (ice_cast_2, 15 frames, box: [-58, -100, 115, 123])
* - IceCastNew03.dcc (ice_cast_3, 16 frames, box: [-63, -113, 127, 148])
* - LightningCast.dcc (light_cast_1, 10 frames, box: [-79, -107, 164, 144])
* - LightningCastRunesFront.dcc (light_cast_2, 10 frames, box: [-73, -154, 147, 190])
* - Teleport.dcc (teleport, 18 frames, box: [-61, -108, 136, 154])
*
* Bakes them into indexed-color PNG atlases and JSON metadata under:
* - public/overlays/
* - samples/d2-packs/overlays/
*
* And generates typed metadata in src/render/overlays-meta.ts.
* Enforces Zero Runtime MPQ/DLL Architectural Invariant.
*/
import { mkdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { MpqArchive } from '../src/mpq/archive.ts'
import { fileSource } from '../src/mpq/file-source.ts'
import { decodeDcc } from '../src/formats/dcc.ts'
import { decodePl2 } from '../src/formats/pl2.ts'
import { packSheetToPng } from './pack-entity-assets.ts'
import type { SpriteFrame, SpriteSheet } from '../src/formats/sprite.ts'
export interface OverlayFrameMeta {
readonly x: number
readonly y: number
readonly w: number
readonly h: number
readonly anchorX: number
readonly anchorY: number
}
export type OverlayFrameTuple = readonly [
x: number,
y: number,
width: number,
height: number,
anchorX: number,
anchorY: number,
]
export interface OverlayBox {
readonly left: number
readonly top: number
readonly width: number
readonly height: number
}
export interface OverlayMeta {
readonly name: string
readonly celFile: string
readonly width: number
readonly height: number
readonly directions: number
readonly frames: number
readonly framesPerDirection: number
readonly animRate: number
readonly trans: number
readonly preDraw: boolean
readonly lightRadius: number
readonly lightColor: readonly [number, number, number]
readonly box: OverlayBox
readonly groups: readonly (readonly OverlayFrameTuple[])[]
}
export const OVERLAY_TARGETS = [
{
name: 'fire_cast_1',
celFile: 'FireCast_for_Sorceress',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [255, 178, 64] as const,
},
{
name: 'fire_cast_2',
celFile: 'FireCast2',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [255, 178, 64] as const,
},
{
name: 'ice_cast_1',
celFile: 'IceCastNew01',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [81, 81, 255] as const,
},
{
name: 'ice_cast_2',
celFile: 'IceCastNew02',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [81, 81, 255] as const,
},
{
name: 'ice_cast_3',
celFile: 'IceCastNew03',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [81, 81, 255] as const,
},
{
name: 'light_cast_1',
celFile: 'LightningCast',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [255, 255, 255] as const,
},
{
name: 'light_cast_2',
celFile: 'LightningCastRunesFront',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 9,
lightColor: [255, 255, 255] as const,
},
{
name: 'teleport',
celFile: 'Teleport',
animRate: 16,
trans: 3,
preDraw: false,
lightRadius: 5,
lightColor: [255, 255, 200] as const,
},
] as const
export async function bakeOverlays(projectRoot: string = process.cwd()): Promise<Record<string, OverlayMeta>> {
const mpqPath = join(projectRoot, 'samples', 'd2', 'd2data.mpq')
const mpq = await MpqArchive.open(await fileSource(mpqPath))
const palFile = mpq.find('data/global/palette/ACT1/pal.pl2')
if (!palFile) throw new Error('data/global/palette/ACT1/pal.pl2 not found in d2data.mpq')
const pl2 = decodePl2(await mpq.read(palFile))
const publicOutDir = join(projectRoot, 'public', 'overlays')
const packOutDir = join(projectRoot, 'samples', 'd2-packs', 'overlays')
mkdirSync(publicOutDir, { recursive: true })
mkdirSync(packOutDir, { recursive: true })
const metaRecord: Record<string, OverlayMeta> = {}
for (const target of OVERLAY_TARGETS) {
const f = mpq.find(`data/global/overlays/${target.celFile}.dcc`)
if (!f) throw new Error(`Overlay DCC not found: data/global/overlays/${target.celFile}.dcc`)
const dcc = decodeDcc(await mpq.read(f))
const firstDir = dcc.directions[0]
if (!firstDir) throw new Error(`Overlay DCC has no directions: ${target.celFile}`)
const box: OverlayBox = {
left: firstDir.box.left,
top: firstDir.box.top,
width: firstDir.box.width,
height: firstDir.box.height,
}
const groups = dcc.directions.map(dir => ({
frames: dir.frames.map(
(frame): SpriteFrame => ({
width: frame.frame.width,
height: frame.frame.height,
indices: frame.frame.indices,
mask: frame.frame.mask,
anchorX: dir.box.left,
anchorY: dir.box.top,
}),
),
}))
const sheet: SpriteSheet = { groups, width: null }
const packed = packSheetToPng(sheet, pl2.rgb)
const meta: OverlayMeta = {
name: target.name,
celFile: target.celFile,
width: packed.width,
height: packed.height,
directions: dcc.directions.length,
frames: firstDir.frames.length,
framesPerDirection: firstDir.frames.length,
animRate: target.animRate,
trans: target.trans,
preDraw: target.preDraw,
lightRadius: target.lightRadius,
lightColor: target.lightColor,
box,
groups: packed.groups,
}
metaRecord[target.name] = meta
metaRecord[target.celFile] = meta
const jsonStr = JSON.stringify(meta, null, 2)
// Write primary name (e.g. fire_cast_1.png) and celFile alias (e.g. FireCast_for_Sorceress.png)
for (const fileKey of [target.name, target.celFile]) {
writeFileSync(join(publicOutDir, `${fileKey}.png`), packed.png)
writeFileSync(join(publicOutDir, `${fileKey}.json`), jsonStr, 'utf-8')
writeFileSync(join(packOutDir, `${fileKey}.png`), packed.png)
writeFileSync(join(packOutDir, `${fileKey}.json`), jsonStr, 'utf-8')
}
console.log(
`[Overlay Packer] Baked ${target.name} (${target.celFile}.dcc): ` +
`${meta.frames} frames, box [${box.left}, ${box.top}, ${box.width}, ${box.height}], ` +
`${packed.width}x${packed.height}, ${packed.png.byteLength} bytes PNG`,
)
}
// Generate src/render/overlays-meta.ts
const metaTsContent = `/**
* Authentic Diablo II v1.13c Cast Overlay Atlas Metadata.
* Auto-generated by scripts/pack-overlays.ts.
* Enforces Zero Runtime MPQ/DLL Architectural Invariant.
*/
export interface OverlayFrameMeta {
readonly x: number
readonly y: number
readonly w: number
readonly h: number
readonly anchorX: number
readonly anchorY: number
}
export type OverlayFrameTuple = readonly [
x: number,
y: number,
width: number,
height: number,
anchorX: number,
anchorY: number,
]
export interface OverlayBox {
readonly left: number
readonly top: number
readonly width: number
readonly height: number
}
export interface OverlayMeta {
readonly name: string
readonly celFile: string
readonly width: number
readonly height: number
readonly directions: number
readonly frames: number
readonly framesPerDirection: number
readonly animRate: number
readonly trans: number
readonly preDraw: boolean
readonly lightRadius: number
readonly lightColor: readonly [number, number, number]
readonly box: OverlayBox
readonly groups: readonly (readonly OverlayFrameTuple[])[]
}
export const FIRE_CAST_1_META: OverlayMeta = ${JSON.stringify(metaRecord['fire_cast_1'], null, 2)}
export const FIRE_CAST_2_META: OverlayMeta = ${JSON.stringify(metaRecord['fire_cast_2'], null, 2)}
export const ICE_CAST_1_META: OverlayMeta = ${JSON.stringify(metaRecord['ice_cast_1'], null, 2)}
export const ICE_CAST_2_META: OverlayMeta = ${JSON.stringify(metaRecord['ice_cast_2'], null, 2)}
export const ICE_CAST_3_META: OverlayMeta = ${JSON.stringify(metaRecord['ice_cast_3'], null, 2)}
export const LIGHT_CAST_1_META: OverlayMeta = ${JSON.stringify(metaRecord['light_cast_1'], null, 2)}
export const LIGHT_CAST_2_META: OverlayMeta = ${JSON.stringify(metaRecord['light_cast_2'], null, 2)}
export const TELEPORT_META: OverlayMeta = ${JSON.stringify(metaRecord['teleport'], null, 2)}
export const OVERLAY_METAS: Readonly<Record<string, OverlayMeta>> = {
fire_cast_1: FIRE_CAST_1_META,
FireCast_for_Sorceress: FIRE_CAST_1_META,
fire_cast_2: FIRE_CAST_2_META,
FireCast2: FIRE_CAST_2_META,
ice_cast_1: ICE_CAST_1_META,
IceCastNew01: ICE_CAST_1_META,
ice_cast_2: ICE_CAST_2_META,
IceCastNew02: ICE_CAST_2_META,
ice_cast_3: ICE_CAST_3_META,
IceCastNew03: ICE_CAST_3_META,
light_cast_1: LIGHT_CAST_1_META,
LightningCast: LIGHT_CAST_1_META,
light_cast_2: LIGHT_CAST_2_META,
LightningCastRunesFront: LIGHT_CAST_2_META,
teleport: TELEPORT_META,
Teleport: TELEPORT_META,
}
`
const metaTsPath = join(projectRoot, 'src', 'render', 'overlays-meta.ts')
writeFileSync(metaTsPath, metaTsContent, 'utf-8')
console.log(`[Overlay Packer] Wrote ${metaTsPath}`)
return metaRecord
}
// Execute standalone if called from CLI
if (process.argv[1]?.endsWith('pack-overlays.ts')) {
bakeOverlays().catch((err: unknown) => {
console.error('Bake overlays failed:', err)
process.exit(1)
})
}

View File

@ -13,7 +13,7 @@ export const DEMO_EXPERIENCE: readonly number[] = [0, 0, 20, 60, 140, 280]
/** Fallback skills, used when an archive ships no skill table. */
export const DEMO_SKILLS: readonly SkillDef[] = [
{ id: 'attack', name: 'Attack', manaCost: 0, cooldownTicks: 10, range: 48, projectile: false, speed: 0, baseMinDamage: 4, baseMaxDamage: 6, damagePerLevel: 1, radius: 20 },
{ id: 'firebolt', name: 'Fire Bolt', manaCost: 6, cooldownTicks: 20, range: 1000, projectile: true, speed: 500, baseMinDamage: 7, baseMaxDamage: 10, damagePerLevel: 3, radius: 20 },
{ id: 'firebolt', name: 'Fire Bolt', manaCost: 6, cooldownTicks: 20, range: 1000, projectile: true, speed: 500, baseMinDamage: 7, baseMaxDamage: 10, damagePerLevel: 3, radius: 20, castOverlay: 'fire_cast_1' },
]
/** Fallback NPC, used when an archive ships no NPC table. */

View File

@ -2,8 +2,8 @@ import { tickCombat, createWorld, spawnMonsters, spawnMonsterPacks, damageMonste
import type { CombatWorld, CombatOptions, MonsterPack, MonsterStats, SafeZone } from './combat.ts'
import { Inventory, rollDrop, goldItem, totalStat } from './items.ts'
import type { Item, ItemBase, Affix } from './items.ts'
import { castSkill, tickProjectiles } from './skills.ts'
import type { SkillDef, Projectile } from './skills.ts'
import { castSkill, tickProjectiles, createActiveOverlay, getSkillCastOverlay } from './skills.ts'
import type { SkillDef, Projectile, ActiveOverlay } from './skills.ts'
import { QuestLog, npcDialog } from './quests.ts'
import type { NpcDef, QuestDef } from './quests.ts'
import { Rng } from './rng.ts'
@ -182,6 +182,7 @@ export class GameEngine {
questLog: QuestLog
projectiles: Projectile[] = []
explosions: ActiveExplosion[] = []
overlays: ActiveOverlay[] = []
castRng: Rng
skillCooldown = 0
selectedSkill = 0
@ -267,6 +268,31 @@ export class GameEngine {
this.opts.monsterKinds = dropTables.monsterKinds
}
/**
* Spawns an active cast overlay effect on an entity (defaulting to the player).
*/
/**
* Spawns an active cast overlay effect on an entity (defaulting to the player).
* Replaces any existing active overlay for the same caster to prevent oversaturated additive stacking.
*/
spawnOverlay(
overlayName: string,
caster?: { x: number; y: number },
casterId = 'player',
): ActiveOverlay {
const pos = caster ?? { x: this.world.player.x, y: this.world.player.y }
// Cleanly replace any ongoing cast overlay for this caster to prevent oversaturated additive stacking
for (const ov of this.overlays) {
if (ov.casterId === casterId) {
ov.expired = true
}
}
this.overlays = this.overlays.filter(ov => !ov.expired)
const overlay = createActiveOverlay(overlayName, pos, casterId)
this.overlays.push(overlay)
return overlay
}
tick(input: EngineInput): void {
const movement = input.movement
const player = this.world.player
@ -292,11 +318,19 @@ export class GameEngine {
this.skillCooldown = wanted.cooldownTicks
this.metrics.casts += 1
castHappened = true
const overlayName = wanted.castOverlay ?? getSkillCastOverlay(wanted.id)
if (overlayName) {
this.spawnOverlay(overlayName, { x: player.x, y: player.y }, 'player')
}
} else if (result.kind === 'instant') {
player.mana = Math.max(0, player.mana - wanted.manaCost)
this.skillCooldown = wanted.cooldownTicks
this.metrics.casts += 1
castHappened = true
const overlayName = wanted.castOverlay ?? getSkillCastOverlay(wanted.id)
if (overlayName) {
this.spawnOverlay(overlayName, { x: player.x, y: player.y }, 'player')
}
this.world.monsters.forEach((monster, index) => {
if (monster.state === 'dead') return
if (Math.hypot(monster.x - player.x, monster.y - player.y) > wanted.radius + 20) return
@ -376,6 +410,20 @@ export class GameEngine {
this.explosions = this.explosions.filter(exp => exp.frame < exp.maxFrames)
}
if (this.overlays.length > 0) {
for (const overlay of this.overlays) {
if (overlay.casterId === 'player') {
overlay.x = player.x
overlay.y = player.y
}
overlay.frame += overlay.animRate / 25
if (overlay.frame >= overlay.maxFrames) {
overlay.expired = true
}
}
this.overlays = this.overlays.filter(overlay => !overlay.expired)
}
if (input.talking) {
let nearest: { def: NpcDef; x: number; y: number } | null = null
let best = this.opts.talkRadius
@ -552,6 +600,8 @@ export class GameEngine {
this.questLog = pieces.quests
this.ground = pieces.ground
this.projectiles = []
this.explosions = []
this.overlays = []
if (this.streamingManager !== undefined) {
this.streamingManager.world = this.world
}

View File

@ -50,6 +50,8 @@ export interface SkillDef {
readonly damagePerLevel: number
/** Radius of the instant effect, for non-projectile skills. */
readonly radius: number
/** Cast overlay identifier from Skills.txt castoverlay column (e.g. 'fire_cast_1'). */
readonly castOverlay?: string | undefined
}
/** A projectile in flight. */
@ -375,6 +377,550 @@ export function getMissileTxtData(
return getMissileTxtData(missileName, undefined)
}
/**
* Data extracted from Diablo II 1.13c Overlay.txt for visual overlay effects.
*/
export interface OverlayTxtData {
readonly overlay: string
readonly filename: string
readonly frames: number
readonly animRate: number
readonly trans: number
readonly preDraw: boolean
readonly initRadius: number
readonly radius: number
readonly red: number
readonly green: number
readonly blue: number
readonly numDirections?: number
readonly xOffset?: number
readonly yOffset?: number
}
/** Canonical 1.13c Ground Truth Overlay.txt defaults for elemental sorceress spell overlays. */
export const CANONICAL_113C_OVERLAYS: Readonly<Record<string, OverlayTxtData>> = Object.freeze({
fire_cast_1: {
overlay: 'fire_cast_1',
filename: 'FireCast_for_Sorceress',
frames: 14,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 178,
blue: 64,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
firecast_for_sorceress: {
overlay: 'fire_cast_1',
filename: 'FireCast_for_Sorceress',
frames: 14,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 178,
blue: 64,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
fire_cast_2: {
overlay: 'fire_cast_2',
filename: 'FireCast2',
frames: 16,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 178,
blue: 64,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
firecast2: {
overlay: 'fire_cast_2',
filename: 'FireCast2',
frames: 16,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 178,
blue: 64,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
ice_cast_1: {
overlay: 'ice_cast_1',
filename: 'IceCastNew01',
frames: 15,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 81,
green: 81,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
icecastnew01: {
overlay: 'ice_cast_1',
filename: 'IceCastNew01',
frames: 15,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 81,
green: 81,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
ice_cast_2: {
overlay: 'ice_cast_2',
filename: 'IceCastNew02',
frames: 15,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 81,
green: 81,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
icecastnew02: {
overlay: 'ice_cast_2',
filename: 'IceCastNew02',
frames: 15,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 81,
green: 81,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
ice_cast_3: {
overlay: 'ice_cast_3',
filename: 'IceCastNew03',
// Note: Overlay.txt lists 15 frames, but IceCastNew03.dcc contains 16 full frames.
frames: 16,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 81,
green: 81,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
icecastnew03: {
overlay: 'ice_cast_3',
filename: 'IceCastNew03',
// Note: Overlay.txt lists 15 frames, but IceCastNew03.dcc contains 16 full frames.
frames: 16,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 81,
green: 81,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
light_cast_1: {
overlay: 'light_cast_1',
filename: 'LightningCast',
frames: 10,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 255,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
lightningcast: {
overlay: 'light_cast_1',
filename: 'LightningCast',
frames: 10,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 255,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
light_cast_2: {
overlay: 'light_cast_2',
filename: 'LightningCastRunesFront',
frames: 10,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 255,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
lightningcastrunesfront: {
overlay: 'light_cast_2',
filename: 'LightningCastRunesFront',
frames: 10,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 255,
blue: 255,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
teleport: {
overlay: 'teleport',
filename: 'Teleport',
frames: 18,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 5,
red: 255,
green: 255,
blue: 200,
numDirections: 1,
xOffset: 0,
yOffset: 0,
},
})
/**
* Parses raw Overlay.txt text into a Map of overlay records.
*/
export function parseOverlayTxt(content: string): Map<string, OverlayTxtData> {
const map = new Map<string, OverlayTxtData>()
const lines = content.split(/\r?\n/).filter(line => line.trim().length > 0)
if (lines.length < 2) return map
const headers = lines[0]!.split('\t').map(h => h.trim())
const col = (name: string) => headers.findIndex(h => h.toLowerCase() === name.toLowerCase())
const overlayIdx = col('overlay')
const filenameIdx = col('Filename')
const framesIdx = col('Frames')
const animRateIdx = col('AnimRate')
const transIdx = col('Trans')
const preDrawIdx = col('PreDraw')
const initRadiusIdx = col('InitRadius')
const radiusIdx = col('Radius')
const redIdx = col('Red')
const greenIdx = col('Green')
const blueIdx = col('Blue')
const numDirIdx = col('NumDirections')
const xOffsetIdx = col('Xoffset')
const yOffsetIdx = col('Yoffset')
for (let i = 1; i < lines.length; i++) {
const cells = lines[i]!.split('\t')
const rawName = (cells[overlayIdx] ?? '').trim()
const name = rawName.toLowerCase()
if (!name || name === 'expansion') continue
const filename = (cells[filenameIdx] ?? '').trim()
const frames = Number(cells[framesIdx] ?? 1) || 1
const animRate = Number(cells[animRateIdx] ?? 16) || 16
const trans = Number(cells[transIdx] ?? 0) || 0
const preDraw = Number(cells[preDrawIdx] ?? 0) !== 0
const initRadius = Number(cells[initRadiusIdx] ?? 0) || 0
const radius = Number(cells[radiusIdx] ?? 0) || 0
const red = Number(cells[redIdx] ?? 255) || 255
const green = Number(cells[greenIdx] ?? 255) || 255
const blue = Number(cells[blueIdx] ?? 255) || 255
const numDirections = Number(cells[numDirIdx] ?? 1) || 1
const xOffset = Number(cells[xOffsetIdx] ?? 0) || 0
const yOffset = Number(cells[yOffsetIdx] ?? 0) || 0
const rec: OverlayTxtData = {
overlay: rawName,
filename,
frames,
animRate,
trans,
preDraw,
initRadius,
radius,
red,
green,
blue,
numDirections,
xOffset,
yOffset,
}
map.set(name, rec)
if (filename) {
map.set(filename.toLowerCase(), rec)
}
}
return map
}
/**
* Extracts overlay data from an Overlay.txt table or parsed records.
*/
export function getOverlayTxtData(
overlayName: string,
overlaySource?:
| Map<string, OverlayTxtData>
| { rows: readonly Readonly<Record<string, string>>[] }
| { header: readonly string[]; rows: readonly (readonly string[])[] }
| string
| undefined,
): OverlayTxtData {
const key = overlayName.trim().toLowerCase()
if (!overlaySource) {
return CANONICAL_113C_OVERLAYS[key] ?? {
overlay: overlayName,
filename: overlayName,
frames: 14,
animRate: 16,
trans: 3,
preDraw: false,
initRadius: 1,
radius: 9,
red: 255,
green: 178,
blue: 64,
}
}
if (typeof overlaySource === 'string') {
const parsed = parseOverlayTxt(overlaySource)
return parsed.get(key) ?? getOverlayTxtData(overlayName, undefined)
}
if (overlaySource instanceof Map) {
return overlaySource.get(key) ?? getOverlayTxtData(overlayName, undefined)
}
// DataTable ({ rows: Record<string, string>[] })
if ('rows' in overlaySource && !('header' in overlaySource)) {
const tableRows = overlaySource.rows as readonly Readonly<Record<string, string>>[]
const row = tableRows.find(
r => (r['overlay'] ?? r['Overlay'] ?? '').trim().toLowerCase() === key ||
(r['Filename'] ?? r['filename'] ?? '').trim().toLowerCase() === key,
)
if (row) {
return {
overlay: (row['overlay'] ?? row['Overlay'] ?? key).trim(),
filename: (row['Filename'] ?? row['filename'] ?? '').trim(),
frames: Number(row['Frames'] ?? row['frames'] ?? 1) || 1,
animRate: Number(row['AnimRate'] ?? row['animrate'] ?? 16) || 16,
trans: Number(row['Trans'] ?? row['trans'] ?? 0) || 0,
preDraw: Number(row['PreDraw'] ?? row['predraw'] ?? 0) !== 0,
initRadius: Number(row['InitRadius'] ?? row['initradius'] ?? 0) || 0,
radius: Number(row['Radius'] ?? row['radius'] ?? 0) || 0,
red: Number(row['Red'] ?? row['red'] ?? 255) || 255,
green: Number(row['Green'] ?? row['green'] ?? 255) || 255,
blue: Number(row['Blue'] ?? row['blue'] ?? 255) || 255,
numDirections: Number(row['NumDirections'] ?? row['numdirections'] ?? 1) || 1,
xOffset: Number(row['Xoffset'] ?? row['xoffset'] ?? 0) || 0,
yOffset: Number(row['Yoffset'] ?? row['yoffset'] ?? 0) || 0,
}
}
}
return getOverlayTxtData(overlayName, undefined)
}
/**
* An active unit overlay in the world (e.g. cast overlay, aura, buff visual).
*/
export interface ActiveOverlay {
readonly id: string
readonly overlayName: string
readonly casterId: string
x: number
y: number
frame: number
readonly maxFrames: number
readonly animRate: number
readonly preDraw: boolean
readonly trans: number
readonly lightRadius: number
readonly lightColor: readonly [number, number, number]
expired: boolean
}
/**
* Creates an ActiveOverlay entity initialized at the caster's coordinates.
*/
export function createActiveOverlay(
overlayName: string,
caster: { x: number; y: number },
casterId = 'player',
overlaySource?: Map<string, OverlayTxtData> | string,
): ActiveOverlay {
const data = getOverlayTxtData(overlayName, overlaySource)
return {
id: `overlay_${overlayName}_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
overlayName: data.overlay,
casterId,
x: caster.x,
y: caster.y,
frame: 0,
maxFrames: data.frames,
animRate: data.animRate,
preDraw: data.preDraw,
trans: data.trans,
lightRadius: data.radius,
lightColor: [data.red, data.green, data.blue] as const,
expired: false,
}
}
/** Complete canonical mapping for all Sorceress skills to their 1.13c castoverlay. */
export const SORCERESS_SKILL_CAST_OVERLAYS: Readonly<Record<number, string>> = Object.freeze({
36: 'fire_cast_1', // Fire Bolt
38: 'light_cast_1', // Charged Bolt
39: 'ice_cast_1', // Ice Bolt
40: 'ice_cast_1', // Frozen Armor
42: 'light_cast_1', // Static Field
43: 'light_cast_2', // Telekinesis
44: 'ice_cast_2', // Frost Nova
45: 'ice_cast_1', // Ice Blast
46: 'fire_cast_2', // Blaze
47: 'fire_cast_2', // Fire Ball
48: 'light_cast_1', // Nova
49: 'light_cast_1', // Lightning
50: 'ice_cast_2', // Shiver Armor
51: 'fire_cast_2', // Fire Wall
53: 'light_cast_1', // Chain Lightning
54: 'teleport', // Teleport
55: 'ice_cast_2', // Glacial Spike
56: 'fire_cast_2', // Meteor
57: 'light_cast_2', // Thunder Storm
58: 'light_cast_2', // Energy Shield
59: 'ice_cast_3', // Blizzard
60: 'ice_cast_3', // Chilling Armor
62: 'fire_cast_2', // Hydra
64: 'ice_cast_3', // Frozen Orb
})
const SORCERESS_SKILL_NAME_TO_OVERLAY: Readonly<Record<string, string>> = Object.freeze({
firebolt: 'fire_cast_1',
'fire bolt': 'fire_cast_1',
chargedbolt: 'light_cast_1',
'charged bolt': 'light_cast_1',
icebolt: 'ice_cast_1',
'ice bolt': 'ice_cast_1',
frozenarmor: 'ice_cast_1',
'frozen armor': 'ice_cast_1',
staticfield: 'light_cast_1',
'static field': 'light_cast_1',
telekinesis: 'light_cast_2',
frostnova: 'ice_cast_2',
'frost nova': 'ice_cast_2',
iceblast: 'ice_cast_1',
'ice blast': 'ice_cast_1',
blaze: 'fire_cast_2',
fireball: 'fire_cast_2',
'fire ball': 'fire_cast_2',
nova: 'light_cast_1',
lightning: 'light_cast_1',
shiverarmor: 'ice_cast_2',
'shiver armor': 'ice_cast_2',
firewall: 'fire_cast_2',
'fire wall': 'fire_cast_2',
chainlightning: 'light_cast_1',
'chain lightning': 'light_cast_1',
teleport: 'teleport',
glacialspike: 'ice_cast_2',
'glacial spike': 'ice_cast_2',
meteor: 'fire_cast_2',
thunderstorm: 'light_cast_2',
'thunder storm': 'light_cast_2',
energyshield: 'light_cast_2',
'energy shield': 'light_cast_2',
blizzard: 'ice_cast_3',
chillingarmor: 'ice_cast_3',
'chilling armor': 'ice_cast_3',
hydra: 'fire_cast_2',
frozenorb: 'ice_cast_3',
'frozen orb': 'ice_cast_3',
})
/**
* Returns canonical castoverlay name for a skill, if any.
*/
export function getSkillCastOverlay(skillId: number | string): string | undefined {
if (typeof skillId === 'number') {
return SORCERESS_SKILL_CAST_OVERLAYS[skillId] ?? BATCH1_SKILLS[skillId]?.castOverlay
}
const num = parseInt(skillId, 10)
if (!Number.isNaN(num)) {
return SORCERESS_SKILL_CAST_OVERLAYS[num] ?? BATCH1_SKILLS[num]?.castOverlay
}
const normalized = skillId.toLowerCase().trim()
const overlay =
SORCERESS_SKILL_NAME_TO_OVERLAY[normalized] ??
SORCERESS_SKILL_NAME_TO_OVERLAY[normalized.replace(/[\s_-]/g, '')]
if (overlay) return overlay
for (const skill of Object.values(BATCH1_SKILLS)) {
if (skill.name.toLowerCase() === normalized && skill.castOverlay) {
return skill.castOverlay
}
}
return undefined
}
/**
* Read one skill from a table row.
*
@ -405,6 +951,8 @@ export function skillFromRow(
if (msl.speedPxPerSec > 0) speed = msl.speedPxPerSec
if (msl.distancePx > 0) range = msl.distancePx
}
const rawCastOverlay = textCell(row, 'castoverlay', textCell(row, 'castOverlay', ''))
const castOverlay = rawCastOverlay.length > 0 ? rawCastOverlay.toLowerCase() : undefined
return {
id,
name: resolveText(rawName, text) || id,
@ -419,6 +967,7 @@ export function skillFromRow(
baseMaxDamage: Math.max(0, numberCell(row, 'MaxDam', numberCell(row, 'maxdam', 2))),
damagePerLevel: numberCell(row, 'PerLevel', numberCell(row, 'LevDam', 0)),
radius: Math.max(1, numberCell(row, 'Radius', numberCell(row, 'HitRadius', 24))),
castOverlay,
}
}
@ -443,6 +992,48 @@ export function skillsFromTable(
return table.rows.map((row, index) => skillFromRow(row, index, text, missilesSource))
}
/**
* Parses raw Skills.txt text into a Map of skill definitions keyed by skill name and ID.
*/
export function parseSkillsTxt(
content: string,
missilesSource?:
| Map<string, MissileTxtData>
| { rows: readonly Readonly<Record<string, string>>[] }
| { header: readonly string[]; rows: readonly (readonly string[])[] }
| string
| undefined,
): Map<string, SkillDef> {
const map = new Map<string, SkillDef>()
const lines = content.split(/\r?\n/).filter(line => line.trim().length > 0)
if (lines.length < 2) return map
const headers = lines[0]!.split('\t').map(h => h.trim())
const rows: Record<string, string>[] = []
for (let i = 1; i < lines.length; i++) {
const cells = lines[i]!.split('\t')
const row: Record<string, string> = {}
for (let c = 0; c < headers.length; c++) {
const colName = headers[c]
if (colName) {
row[colName] = (cells[c] ?? '').trim()
}
}
rows.push(row)
}
for (let i = 0; i < rows.length; i++) {
const row = rows[i]!
const skillName = (row['skill'] ?? row['Name'] ?? row['skilldesc'] ?? `skill${i}`).trim()
if (!skillName || skillName === 'expansion') continue
const def = skillFromRow(row, i, { get: () => skillName }, missilesSource)
map.set(skillName.toLowerCase(), def)
if (def.id) {
map.set(def.id.toLowerCase(), def)
}
}
return map
}
/**
* Damage of a skill at a level.
*
@ -679,6 +1270,7 @@ export interface Batch1SkillDef {
readonly srcDamage: number
readonly range?: number
readonly speed?: number
readonly castOverlay?: string
}
export const BATCH1_SKILLS: Readonly<Record<number, Batch1SkillDef>> = Object.freeze({
@ -690,12 +1282,12 @@ export const BATCH1_SKILLS: Readonly<Record<number, Batch1SkillDef>> = Object.fr
14: { id: 14, name: 'Power Strike', classCode: 'ama', charClass: 'amazon', type: 'melee', mana: 16, lvlmana: 2, minmana: 0, manashift: 5, delay: 0, hitshift: 8, emin: 1, emax: 16, elev1: 8, elev2: 12, elev3: 16, elev4: 20, elev5: 24, srcDamage: 128 },
// Sorceress (6 skills)
36: { id: 36, name: 'Fire Bolt', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 5, lvlmana: 0, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 6, emax: 12, elev1: 2, elev2: 4, elev3: 6, elev4: 8, elev5: 10, srcDamage: 0, range: 1000, speed: 500 },
36: { id: 36, name: 'Fire Bolt', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 5, lvlmana: 0, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 6, emax: 12, elev1: 2, elev2: 4, elev3: 6, elev4: 8, elev5: 10, srcDamage: 0, range: 1000, speed: 500, castOverlay: 'fire_cast_1' },
37: { id: 37, name: 'Warmth', classCode: 'sor', charClass: 'sorceress', type: 'passive', mana: 0, lvlmana: 0, minmana: 0, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0 },
39: { id: 39, name: 'Ice Bolt', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 6, lvlmana: 0, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 6, emax: 10, elev1: 2, elev2: 4, elev3: 6, elev4: 8, elev5: 10, srcDamage: 0, range: 400, speed: 450 },
44: { id: 44, name: 'Frost Nova', classCode: 'sor', charClass: 'sorceress', type: 'area', mana: 9, lvlmana: 1, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 2, emax: 4, elev1: 1, elev2: 2, elev3: 3, elev4: 4, elev5: 5, srcDamage: 0 },
47: { id: 47, name: 'Fire Ball', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 10, lvlmana: 1, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 12, emax: 28, elev1: 6, elev2: 12, elev3: 18, elev4: 24, elev5: 30, srcDamage: 0, range: 450, speed: 450 },
54: { id: 54, name: 'Teleport', classCode: 'sor', charClass: 'sorceress', type: 'movement', mana: 24, lvlmana: -1, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0 },
39: { id: 39, name: 'Ice Bolt', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 6, lvlmana: 0, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 6, emax: 10, elev1: 2, elev2: 4, elev3: 6, elev4: 8, elev5: 10, srcDamage: 0, range: 400, speed: 450, castOverlay: 'ice_cast_1' },
44: { id: 44, name: 'Frost Nova', classCode: 'sor', charClass: 'sorceress', type: 'area', mana: 9, lvlmana: 1, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 2, emax: 4, elev1: 1, elev2: 2, elev3: 3, elev4: 4, elev5: 5, srcDamage: 0, castOverlay: 'ice_cast_2' },
47: { id: 47, name: 'Fire Ball', classCode: 'sor', charClass: 'sorceress', type: 'projectile', mana: 10, lvlmana: 1, minmana: 1, manashift: 7, delay: 0, hitshift: 7, emin: 12, emax: 28, elev1: 6, elev2: 12, elev3: 18, elev4: 24, elev5: 30, srcDamage: 0, range: 450, speed: 450, castOverlay: 'fire_cast_2' },
54: { id: 54, name: 'Teleport', classCode: 'sor', charClass: 'sorceress', type: 'movement', mana: 24, lvlmana: -1, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0, castOverlay: 'teleport' },
// Necromancer (5 skills)
66: { id: 66, name: 'Amplify Damage', classCode: 'nec', charClass: 'necromancer', type: 'curse', mana: 4, lvlmana: 0, minmana: 1, manashift: 8, delay: 0, hitshift: 8, emin: 0, emax: 0, elev1: 0, elev2: 0, elev3: 0, elev4: 0, elev5: 0, srcDamage: 0 },

1210
src/render/overlays-meta.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -80,6 +80,8 @@ export interface DrawOptions {
* - 4: Champion / Elite Blue Tint
*/
readonly paletteRow?: number
/** Blend mode for this draw call ('normal' or 'additive'). */
readonly blendMode?: 'normal' | 'additive'
}
/** Configuration options and event callbacks for SpriteRenderer. */
@ -260,6 +262,7 @@ export class SpriteRenderer {
/** Listener references for cleanup. */
private readonly handleContextLost: (event: Event) => void
private readonly handleContextRestored: () => void
private currentBlendMode: 'normal' | 'additive' = 'normal'
/**
* @param canvas - the canvas to render into.
@ -423,6 +426,28 @@ export class SpriteRenderer {
return this.maxBatchTextures
}
/** Current blending mode ('normal' or 'additive'). */
get blendMode(): 'normal' | 'additive' {
return this.currentBlendMode
}
/**
* Set the blend mode for subsequent quads.
* Flushes any pending quads before switching GPU blend states.
*/
setBlendMode(mode: 'normal' | 'additive'): void {
if (this.disposed) return
if (this.currentBlendMode === mode) return
this.flush()
this.currentBlendMode = mode
const gl = this.gl
if (mode === 'additive') {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE)
} else {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
}
}
/**
* Upload an atlas, replacing the current one.
*
@ -752,6 +777,7 @@ export class SpriteRenderer {
begin(camera: Camera, clear: readonly [number, number, number] = [0, 0, 0]): void {
if (this.disposed) throw new RendererError('SpriteRenderer has already been disposed')
const gl = this.gl
this.setBlendMode('normal')
this.camera = camera
this.quadCount = 0
this.batchTextureCount = 0
@ -772,6 +798,9 @@ export class SpriteRenderer {
*/
draw(frame: AtlasFrame, x: number, y: number, options: DrawOptions = {}): void {
if (this.disposed) return
if (options.blendMode !== undefined && options.blendMode !== this.currentBlendMode) {
this.setBlendMode(options.blendMode)
}
const page = options.atlas ?? this.defaultAtlas
const unit = this.unitFor(page)
const u0 = frame.x / page.width
@ -802,9 +831,20 @@ export class SpriteRenderer {
* @param width - rectangle width.
* @param height - rectangle height.
* @param color - `[r, g, b, a]` in 0..1.
* @param options - optional draw options like blendMode.
*/
drawSolid(x: number, y: number, width: number, height: number, color: readonly [number, number, number, number]): void {
drawSolid(
x: number,
y: number,
width: number,
height: number,
color: readonly [number, number, number, number],
options: { blendMode?: 'normal' | 'additive' } = {},
): void {
if (this.disposed) return
if (options.blendMode !== undefined && options.blendMode !== this.currentBlendMode) {
this.setBlendMode(options.blendMode)
}
const unit = this.unitFor(this.whiteAtlas)
this.quad(x, y, x + width, y + height, 0.5, 0.5, 0.5, 0.5, color[0], color[1], color[2], color[3], unit, -1)
}

View File

@ -45,8 +45,8 @@ import { getEmbeddedDropTables } from '../game/embedded-drop-tables.ts'
import { buildNpcDef, hasPackedSprite } from '../game/npc.ts'
import { npcDialog } from '../game/quests.ts'
import type { NpcDef } from '../game/quests.ts'
import { BATCH1_SKILLS, getBatch1SkillDef, isBatch1Skill, getSkillManaCost, getMissileTxtData } from '../game/skills.ts'
import type { Projectile } from '../game/skills.ts'
import { BATCH1_SKILLS, getBatch1SkillDef, isBatch1Skill, getSkillManaCost, getMissileTxtData, getSkillCastOverlay } from '../game/skills.ts'
import type { Projectile, ActiveOverlay } from '../game/skills.ts'
import { findSafeDropPosition, calculateBounceHeight, type GroundItemEntity } from '../game/ground-items.ts'
import {
GroundLabelOverlay,
@ -61,6 +61,7 @@ import { RoofFadeController } from '../render/roofs.ts'
import { facingToDirection } from '../game/character.ts'
import type { LoadedMonsterArt } from '../game/monster-art.ts'
import type { MissileMeta } from '../render/missiles-meta.ts'
import type { OverlayMeta } from '../render/overlays-meta.ts'
import { velocityToDccDirection } from '../render/missiles-meta.ts'
import { ActorAnimator, type AnimClipMeta } from '../game/actor-animator.ts'
import { resolveMonsterArtSpec } from '../game/monster-mapping.ts'
@ -1007,7 +1008,15 @@ export function castSkill(
}
}
// 5. Dispatch skill
// 5. Spawn cast overlay if defined for this skill
if (skillId !== 0) {
const castOverlayName = getSkillCastOverlay(skillId)
if (castOverlayName) {
engine.spawnOverlay(castOverlayName, { x: player.x, y: player.y }, 'player')
}
}
// 6. Dispatch skill
if (skillId === 0) {
const weapon = context.equippedWeapon ?? hudManager?.inventory?.equipped?.weapon1 ?? (typeof window !== 'undefined' ? (window as any).__d2webHudInstance?.inventory?.equipped?.weapon1 : null)
const weaponInfo = getAttackWeaponInfo(weapon)
@ -1102,6 +1111,12 @@ export function castSkill(
if (!blocked && overlap === 0) {
player.x = targetX
player.y = targetY
for (const ov of engine.overlays) {
if (ov.casterId === 'player') {
ov.x = player.x
ov.y = player.y
}
}
status.textContent = '已施展传送'
} else {
status.textContent = '无法传送到该位置'
@ -3126,6 +3141,153 @@ export function drawFireExplosion(
}
}
/**
* Packed overlay animation asset loaded into GPU memory.
*/
export interface LoadedOverlayArt {
readonly meta: OverlayMeta
readonly handle: AtlasHandle
readonly frames: AtlasFrame[][]
}
/**
* Load authentic 1.13c baked cast overlay atlases from /overlays/*.png or packBase.
*/
export async function loadOverlayArtMap(
renderer: SpriteRenderer,
packBase?: string,
): Promise<Map<string, LoadedOverlayArt>> {
const map = new Map<string, LoadedOverlayArt>()
if (typeof fetch === 'undefined' || typeof createImageBitmap === 'undefined') return map
const targets = [
'fire_cast_1',
'fire_cast_2',
'ice_cast_1',
'ice_cast_2',
'ice_cast_3',
'light_cast_1',
'light_cast_2',
'teleport',
]
const baseCandidates = [
'/overlays',
packBase ? `${packBase}/overlays` : '',
'samples/d2-packs/overlays',
].filter(Boolean)
await Promise.all(
targets.map(async name => {
for (const base of baseCandidates) {
try {
const [jsonResp, pngResp] = await Promise.all([
fetch(`${base}/${name}.json`),
fetch(`${base}/${name}.png`),
])
if (jsonResp.ok && pngResp.ok) {
const meta = (await jsonResp.json()) as OverlayMeta
const blob = await pngResp.blob()
const bitmap = await createImageBitmap(blob)
let handle: AtlasHandle
try {
handle = renderer.addAtlas(bitmap, meta.width, meta.height)
} finally {
bitmap.close()
}
const frames: AtlasFrame[][] = meta.groups.map(group =>
group.map(([x, y, width, height, anchorX, anchorY]) => ({
x,
y,
width,
height,
...(anchorX !== undefined ? { anchorX } : {}),
...(anchorY !== undefined ? { anchorY } : {}),
})),
)
const art: LoadedOverlayArt = { meta, handle, frames }
map.set(name, art)
if (meta.celFile) {
map.set(meta.celFile.toLowerCase(), art)
}
break
}
} catch {
// ignore candidate error and continue to next fallback path
}
}
}),
)
return map
}
/**
* Render a Diablo II 1.13c character cast overlay effect:
* - Uses authentic baked DCC sprite atlas with additive blending (Trans === 3) when available.
* - Respects frame anchorX and anchorY offsets to align with caster's origin.
* - Projects a dynamic point light with authentic 1.13c light colors and radius.
* - Falls back to procedural magic glow in test/offline environments.
*/
export function drawCastOverlay(
renderer: SpriteRenderer,
overlay: ActiveOverlay,
overlayArt?: LoadedOverlayArt,
): void {
// 1. Dynamic point light halo (1.13c colors & radius)
const [r255, g255, b255] = overlay.lightColor
const r = r255 / 255
const g = g255 / 255
const b = b255 / 255
const radius = overlay.lightRadius > 0 ? overlay.lightRadius * 5 : 45
const progress = Math.min(1, Math.max(0, overlay.frame / overlay.maxFrames))
const intensity = Math.sin(progress * Math.PI) * 0.35 + 0.15
if (intensity > 0.01) {
renderer.drawSolid(
overlay.x - radius,
overlay.y - radius,
radius * 2,
radius * 2,
[r, g, b, intensity * 0.25],
{ blendMode: 'additive' },
)
const coreRadius = radius * 0.5
renderer.drawSolid(
overlay.x - coreRadius,
overlay.y - coreRadius,
coreRadius * 2,
coreRadius * 2,
[r, g, b, intensity * 0.5],
{ blendMode: 'additive' },
)
}
// 2. Sprite animation with additive blending
if (overlayArt !== undefined) {
const frameCount = Math.max(1, overlayArt.meta.framesPerDirection || overlayArt.meta.frames)
const frameIndex = Math.min(Math.max(0, Math.floor(overlay.frame)), frameCount - 1)
const group = overlayArt.frames[0]
const frame = group?.[frameIndex] ?? group?.[0]
if (frame !== undefined) {
const drawX = frame.anchorX !== undefined ? overlay.x + frame.anchorX : overlay.x - frame.width / 2
const drawY = frame.anchorY !== undefined ? overlay.y + frame.anchorY : overlay.y - frame.height / 2
renderer.draw(frame, drawX, drawY, { atlas: overlayArt.handle, blendMode: 'additive' })
renderer.setBlendMode('normal')
return
}
}
// 3. Fallback procedural representation in test/offline environments
const fallbackRadius = 16
renderer.drawSolid(
overlay.x - fallbackRadius,
overlay.y - fallbackRadius,
fallbackRadius * 2,
fallbackRadius * 2,
[r, g, b, 0.6],
{ blendMode: 'additive' },
)
renderer.setBlendMode('normal')
}
/** Quality colors for ground item highlights and glints. */
export const GROUND_ITEM_QUALITY_COLORS: Record<string, readonly [number, number, number, number]> = {
normal: [0.88, 0.88, 0.88, 1.0],
@ -3262,6 +3424,10 @@ async function runScene(initialRuntime: MapRuntime, renderer: SpriteRenderer, st
void loadMissileArtMap(renderer, packEntityBase).then(map => {
missileArtMap = map
})
let overlayArtMap = new Map<string, LoadedOverlayArt>()
void loadOverlayArtMap(renderer, packEntityBase).then(map => {
overlayArtMap = map
})
let monsterWalkFrame = 0
const playerAnimator = new ActorAnimator()
@ -4200,9 +4366,9 @@ if (typeof window !== 'undefined') {
const entities: EntityDraw[] = []
const pushEntity = (x: number, y: number, draw: () => void) => {
const pushEntity = (x: number, y: number, draw: () => void, depthOffset = 0) => {
const c = cellOf(runtime.grid, x, y)
entities.push({ x, y, depth: c.x + c.y, draw })
entities.push({ x, y, depth: c.x + c.y + depthOffset, draw })
}
pushEntity(engine.world.player.x, engine.world.player.y, () => {
@ -4320,6 +4486,22 @@ if (typeof window !== 'undefined') {
})
}
for (const overlay of engine.overlays) {
if (overlay.expired) continue
pushEntity(
overlay.x,
overlay.y,
() => {
drawCastOverlay(
renderer,
overlay,
overlayArtMap.get(overlay.overlayName) ?? overlayArtMap.get(overlay.overlayName.toLowerCase()),
)
},
overlay.preDraw ? -0.1 : 0.1,
)
}
for (const gItem of engine.groundItems.all) {
pushEntity(gItem.x, gItem.y, () => {
drawGroundItem(renderer, gItem, renderStarted)

View File

@ -0,0 +1,758 @@
/**
* Diablo II: Lord of Destruction v1.13c — Cast Overlay System Parity Test Suite
*
* Requirements (Issue #386):
* 1. 1.13c Ground Truth: Overlay.txt & Skills.txt authentic data extraction & verification.
* 2. Complete mapping for all 24 Sorceress skills to their 1.13c castoverlay entries.
* 3. Exact entity lifecycle: ActiveOverlay frame progression (animRate / 25 = 0.64/tick) & expiration (tick 22 for 14 frames).
* 4. Spatial tracking: ActiveOverlay follows caster movement and instant teleportation.
* 5. Viewport rendering: SpriteRenderer additive blending (Trans === 3, SRC_ALPHA + ONE), anchor alignment, & point lighting.
* 6. Offline asset pipeline verification: Baked PNG/JSON in public/overlays/ and samples/d2-packs/overlays/.
*/
import { describe, expect, it } from 'vitest'
import fs from 'node:fs'
import path from 'node:path'
import { Rng } from '../src/game/rng.ts'
import {
parseOverlayTxt,
getOverlayTxtData,
createActiveOverlay,
parseSkillsTxt,
getSkillCastOverlay,
SORCERESS_SKILL_CAST_OVERLAYS,
CANONICAL_113C_OVERLAYS,
BATCH1_SKILLS,
type ActiveOverlay,
type OverlayTxtData,
castSkill,
} from '../src/game/skills.ts'
import {
drawCastOverlay,
loadOverlayArtMap,
type LoadedOverlayArt,
} from '../src/scene/act-scene.ts'
import { GameEngine } from '../src/game/engine.ts'
import { DEMO_EXPERIENCE, DEMO_SKILLS, DEMO_QUESTS } from '../src/game/demo-data.ts'
import { SpriteRenderer, type AtlasHandle } from '../src/render/renderer.ts'
import {
FIRE_CAST_1_META,
TELEPORT_META,
OVERLAY_METAS,
} from '../src/render/overlays-meta.ts'
describe('Issue #386 — Diablo II v1.13c Cast Overlay Parity', () => {
const overlayTxtPath = path.resolve(__dirname, '../samples/fixtures/data/global/excel/Overlay.txt')
const skillsTxtPath = path.resolve(__dirname, '../samples/fixtures/data/global/excel/Skills.txt')
const overlayTxtContent = fs.readFileSync(overlayTxtPath, 'latin1')
const skillsTxtContent = fs.readFileSync(skillsTxtPath, 'latin1')
it('1. Parses authentic 1.13c Overlay.txt and verifies elemental cast overlays', () => {
expect(overlayTxtContent.length).toBeGreaterThan(5000)
const overlayMap = parseOverlayTxt(overlayTxtContent)
// Verify fire_cast_1
const fireCast1 = overlayMap.get('fire_cast_1')
expect(fireCast1).toBeDefined()
expect(fireCast1!.filename).toBe('FireCast_for_Sorceress')
expect(fireCast1!.frames).toBe(14)
expect(fireCast1!.animRate).toBe(16)
expect(fireCast1!.trans).toBe(3) // additive blending
expect(fireCast1!.preDraw).toBe(false) // foreground (0 in Overlay.txt)
expect(fireCast1!.radius).toBe(9)
expect(fireCast1!.red).toBe(255)
expect(fireCast1!.green).toBe(178)
expect(fireCast1!.blue).toBe(64)
// Verify fire_cast_2
const fireCast2 = overlayMap.get('fire_cast_2')
expect(fireCast2).toBeDefined()
expect(fireCast2!.filename).toBe('FireCast2')
expect(fireCast2!.frames).toBe(16)
expect(fireCast2!.animRate).toBe(16)
expect(fireCast2!.trans).toBe(3)
expect(fireCast2!.preDraw).toBe(false)
expect(fireCast2!.radius).toBe(9)
// Verify ice_cast_1
const iceCast1 = overlayMap.get('ice_cast_1')
expect(iceCast1).toBeDefined()
expect(iceCast1!.filename).toBe('IceCastNew01')
expect(iceCast1!.frames).toBe(15)
expect(iceCast1!.animRate).toBe(16)
expect(iceCast1!.trans).toBe(3)
expect(iceCast1!.preDraw).toBe(false)
expect(iceCast1!.radius).toBe(9)
expect(iceCast1!.red).toBe(81)
expect(iceCast1!.green).toBe(81)
expect(iceCast1!.blue).toBe(255)
// Verify light_cast_1
const lightCast1 = overlayMap.get('light_cast_1')
expect(lightCast1).toBeDefined()
expect(lightCast1!.filename).toBe('LightningCast')
expect(lightCast1!.frames).toBe(10)
expect(lightCast1!.animRate).toBe(16)
expect(lightCast1!.trans).toBe(3)
expect(lightCast1!.preDraw).toBe(false)
expect(lightCast1!.radius).toBe(9)
expect(lightCast1!.red).toBe(255)
expect(lightCast1!.green).toBe(255)
expect(lightCast1!.blue).toBe(255)
// Verify teleport
const teleport = overlayMap.get('teleport')
expect(teleport).toBeDefined()
expect(teleport!.filename).toBe('Teleport')
expect(teleport!.frames).toBe(18)
expect(teleport!.animRate).toBe(16)
expect(teleport!.trans).toBe(3)
expect(teleport!.preDraw).toBe(false)
expect(teleport!.radius).toBe(5)
expect(teleport!.red).toBe(255)
expect(teleport!.green).toBe(255)
expect(teleport!.blue).toBe(200)
})
it('2. Parses authentic 1.13c Skills.txt and verifies castoverlay for Sorceress skills', () => {
expect(skillsTxtContent.length).toBeGreaterThan(100000)
const skillsMap = parseSkillsTxt(skillsTxtContent)
// Fire Bolt (Id: 36) -> fire_cast_1
const fireBolt = skillsMap.get('36')
expect(fireBolt).toBeDefined()
expect(fireBolt!.castOverlay).toBe('fire_cast_1')
// Charged Bolt (Id: 38) -> light_cast_1
const chargedBolt = skillsMap.get('38')
expect(chargedBolt).toBeDefined()
expect(chargedBolt!.castOverlay).toBe('light_cast_1')
// Ice Bolt (Id: 39) -> ice_cast_1
const iceBolt = skillsMap.get('39')
expect(iceBolt).toBeDefined()
expect(iceBolt!.castOverlay).toBe('ice_cast_1')
// Frost Nova (Id: 44) -> ice_cast_2
const frostNova = skillsMap.get('44')
expect(frostNova).toBeDefined()
expect(frostNova!.castOverlay).toBe('ice_cast_2')
// Fire Ball (Id: 47) -> fire_cast_2
const fireBall = skillsMap.get('47')
expect(fireBall).toBeDefined()
expect(fireBall!.castOverlay).toBe('fire_cast_2')
// Teleport (Id: 54) -> teleport
const teleportSkill = skillsMap.get('54')
expect(teleportSkill).toBeDefined()
expect(teleportSkill!.castOverlay).toBe('teleport')
// Meteor (Id: 56) -> fire_cast_2
const meteor = skillsMap.get('56')
expect(meteor).toBeDefined()
expect(meteor!.castOverlay).toBe('fire_cast_2')
// Frozen Orb (Id: 64) -> ice_cast_3
const frozenOrb = skillsMap.get('64')
expect(frozenOrb).toBeDefined()
expect(frozenOrb!.castOverlay).toBe('ice_cast_3')
})
it('3. Complete canonical mapping matches all 24 Sorceress elemental spells', () => {
const expectedMappings: Record<number, string> = {
36: 'fire_cast_1', // Fire Bolt
38: 'light_cast_1', // Charged Bolt
39: 'ice_cast_1', // Ice Bolt
40: 'ice_cast_1', // Frozen Armor
42: 'light_cast_1', // Static Field
43: 'light_cast_2', // Telekinesis
44: 'ice_cast_2', // Frost Nova
45: 'ice_cast_1', // Ice Blast
46: 'fire_cast_2', // Blaze
47: 'fire_cast_2', // Fire Ball
48: 'light_cast_1', // Nova
49: 'light_cast_1', // Lightning
50: 'ice_cast_2', // Shiver Armor
51: 'fire_cast_2', // Fire Wall
53: 'light_cast_1', // Chain Lightning
54: 'teleport', // Teleport
55: 'ice_cast_2', // Glacial Spike
56: 'fire_cast_2', // Meteor
57: 'light_cast_2', // Thunder Storm
58: 'light_cast_2', // Energy Shield
59: 'ice_cast_3', // Blizzard
60: 'ice_cast_3', // Chilling Armor
62: 'fire_cast_2', // Hydra
64: 'ice_cast_3', // Frozen Orb
}
for (const [idStr, overlay] of Object.entries(expectedMappings)) {
const id = parseInt(idStr, 10)
expect(SORCERESS_SKILL_CAST_OVERLAYS[id]).toBe(overlay)
expect(getSkillCastOverlay(id)).toBe(overlay)
expect(getSkillCastOverlay(String(id))).toBe(overlay)
}
// Verify BATCH1_SKILLS has castOverlay attached
expect(BATCH1_SKILLS[36]?.castOverlay).toBe('fire_cast_1')
expect(BATCH1_SKILLS[39]?.castOverlay).toBe('ice_cast_1')
expect(BATCH1_SKILLS[44]?.castOverlay).toBe('ice_cast_2')
expect(BATCH1_SKILLS[47]?.castOverlay).toBe('fire_cast_2')
expect(BATCH1_SKILLS[54]?.castOverlay).toBe('teleport')
})
it('4. getOverlayTxtData resolves parameters from text, map, or built-in canonical fallback', () => {
const fromTxt = getOverlayTxtData('fire_cast_1', overlayTxtContent)
expect(fromTxt.frames).toBe(14)
expect(fromTxt.animRate).toBe(16)
expect(fromTxt.trans).toBe(3)
expect(fromTxt.radius).toBe(9)
const fromMap = getOverlayTxtData('fire_cast_1', parseOverlayTxt(overlayTxtContent))
expect(fromMap.frames).toBe(14)
expect(fromMap.animRate).toBe(16)
// Guaranteed zero-runtime-MPQ and missing-file fallback
const fallback = getOverlayTxtData('fire_cast_1', undefined)
expect(fallback.frames).toBe(14)
expect(fallback.animRate).toBe(16)
expect(fallback.trans).toBe(3)
expect(fallback.red).toBe(255)
expect(fallback.green).toBe(178)
expect(fallback.blue).toBe(64)
})
it('5. ActiveOverlay entity lifecycle: 1.13c frame progression and exact tick-22 expiration', () => {
const overlay = createActiveOverlay('fire_cast_1', { x: 500, y: 300 }, 'player')
expect(overlay.frame).toBe(0)
expect(overlay.maxFrames).toBe(14)
expect(overlay.animRate).toBe(16)
expect(overlay.preDraw).toBe(false)
expect(overlay.trans).toBe(3)
expect(overlay.lightRadius).toBe(9)
expect(overlay.lightColor).toEqual([255, 178, 64])
expect(overlay.expired).toBe(false)
// Simulate 25 FPS engine progression: step = 16 / 25 = 0.64
const step = overlay.animRate / 25
expect(step).toBe(0.64)
for (let tick = 1; tick <= 21; tick++) {
overlay.frame += step
if (overlay.frame >= overlay.maxFrames) {
overlay.expired = true
}
expect(overlay.expired).toBe(false)
expect(overlay.frame).toBeCloseTo(tick * 0.64, 4)
}
// At tick 21: 21 * 0.64 = 13.44 < 14 -> Not expired
expect(overlay.frame).toBeCloseTo(13.44, 4)
expect(overlay.expired).toBe(false)
// At tick 22: 22 * 0.64 = 14.08 >= 14 -> Expired!
overlay.frame += step
if (overlay.frame >= overlay.maxFrames) {
overlay.expired = true
}
expect(overlay.frame).toBeCloseTo(14.08, 4)
expect(overlay.expired).toBe(true)
})
it('6. GameEngine spawns overlay on skill cast, tracks player, and advances frames', () => {
const terrain = { widthPx: 1000, heightPx: 1000, overlap: () => 0 }
const engine = new GameEngine(terrain, {
spawn: { x: 200, y: 200 },
stats: [],
xpTable: DEMO_EXPERIENCE,
skills: DEMO_SKILLS, // index 1 is firebolt (id: 36)
npcDefs: [],
questDefs: DEMO_QUESTS,
combatOptions: {
playerSpeed: 4,
playerReach: 50,
playerCooldownTicks: 10,
playerDamage: 5,
playerManaPerAttack: 1,
respawnTicks: 100,
},
talkRadius: 50,
pickupRadius: 30,
inventoryCols: 10,
inventoryRows: 4,
monsterCount: 0,
})
expect(engine.overlays.length).toBe(0)
// Select Fire Bolt (skill index 1, digits: [2])
engine.tick({
movement: { x: 0, y: 0 },
attacking: false,
pickingUp: false,
talking: false,
digits: [2],
saving: false,
loading: false,
})
expect(engine.selectedSkill).toBe(1)
expect(engine.opts.skills[engine.selectedSkill]?.id).toBe('firebolt')
// Cast Fire Bolt
engine.tick({
movement: { x: 0, y: 0 },
attacking: true,
pickingUp: false,
talking: false,
digits: [],
saving: false,
loading: false,
})
expect(engine.overlays.length).toBe(1)
const active = engine.overlays[0]!
expect(active.overlayName).toBe('fire_cast_1')
expect(active.casterId).toBe('player')
expect(active.x).toBe(engine.world.player.x)
expect(active.y).toBe(engine.world.player.y)
expect(active.frame).toBeCloseTo(0.64, 4)
// Player moves 10 ticks: overlay must follow player position
for (let i = 0; i < 10; i++) {
engine.tick({
movement: { x: 1, y: 0 },
attacking: false,
pickingUp: false,
talking: false,
digits: [],
saving: false,
loading: false,
})
expect(engine.overlays[0]!.x).toBe(engine.world.player.x)
expect(engine.overlays[0]!.y).toBe(engine.world.player.y)
}
// Advance until expiration (22 ticks total from cast)
// We already ticked 1 (cast tick) + 10 (movement ticks) = 11 ticks.
// Need 11 more ticks to reach tick 22.
for (let i = 0; i < 11; i++) {
engine.tick({
movement: { x: 0, y: 0 },
attacking: false,
pickingUp: false,
talking: false,
digits: [],
saving: false,
loading: false,
})
}
// Overlay has expired and was removed
expect(engine.overlays.length).toBe(0)
})
it('7. SpriteRenderer additive blending mode (Trans === 3) flushes and switches blendFunc', () => {
let nextId = 1
let blendSrc = 0
let blendDst = 0
const gl: any = {
VERTEX_SHADER: 35633,
FRAGMENT_SHADER: 35632,
COMPILE_STATUS: 35713,
LINK_STATUS: 35714,
MAX_TEXTURE_SIZE: 3379,
MAX_TEXTURE_IMAGE_UNITS: 34930,
TEXTURE_2D: 3553,
RGBA: 6408,
RGBA8: 32856,
UNSIGNED_BYTE: 5121,
UNSIGNED_INT: 5125,
FLOAT: 5126,
ARRAY_BUFFER: 34962,
ELEMENT_ARRAY_BUFFER: 34963,
DYNAMIC_DRAW: 35048,
STATIC_DRAW: 35044,
TRIANGLES: 4,
TEXTURE_MIN_FILTER: 10241,
TEXTURE_MAG_FILTER: 10240,
TEXTURE_WRAP_S: 10242,
TEXTURE_WRAP_T: 10243,
CLAMP_TO_EDGE: 33071,
NEAREST: 9728,
UNPACK_ALIGNMENT: 3317,
BLEND: 3042,
SRC_ALPHA: 770,
ONE: 1,
ONE_MINUS_SRC_ALPHA: 771,
DEPTH_TEST: 2929,
COLOR_BUFFER_BIT: 16384,
TEXTURE0: 33984,
drawingBufferWidth: 800,
drawingBufferHeight: 600,
isContextLost: () => false,
createProgram: () => ({ id: nextId++, type: 'program' }),
deleteProgram: () => {},
attachShader: () => {},
detachShader: () => {},
linkProgram: () => {},
getProgramParameter: () => true,
getProgramInfoLog: () => '',
useProgram: () => {},
createShader: (type: number) => ({ id: nextId++, type }),
shaderSource: () => {},
compileShader: () => {},
getShaderParameter: () => true,
getShaderInfoLog: () => '',
deleteShader: () => {},
createVertexArray: () => ({ id: nextId++, type: 'vao' }),
deleteVertexArray: () => {},
bindVertexArray: () => {},
createBuffer: () => ({ id: nextId++, type: 'buffer' }),
deleteBuffer: () => {},
bindBuffer: () => {},
bufferData: () => {},
bufferSubData: () => {},
getAttribLocation: (_p: any, name: string) =>
name === 'a_position' ? 0 : name === 'a_uv' ? 1 : name === 'a_tint' ? 2 : name === 'a_unit' ? 3 : 4,
enableVertexAttribArray: () => {},
vertexAttribPointer: () => {},
createTexture: () => ({ id: nextId++, type: 'texture' }),
deleteTexture: () => {},
bindTexture: () => {},
texParameteri: () => {},
pixelStorei: () => {},
texImage2D: () => {},
getParameter: (param: number) => (param === 3379 ? 4096 : param === 34930 ? 16 : 0),
getUniformLocation: (_p: any, name: string) => ({ name }),
uniform2f: () => {},
uniform1f: () => {},
uniform1i: () => {},
uniform1iv: () => {},
enable: () => {},
disable: () => {},
blendFunc: (src: number, dst: number) => {
blendSrc = src
blendDst = dst
},
viewport: () => {},
clearColor: () => {},
clear: () => {},
activeTexture: () => {},
drawArrays: () => {},
drawElements: () => {},
}
const canvas: any = {
width: 800,
height: 600,
clientWidth: 800,
clientHeight: 600,
getContext: (type: string) => (type === 'webgl2' ? gl : null),
addEventListener: () => {},
removeEventListener: () => {},
}
const renderer = new SpriteRenderer(canvas as HTMLCanvasElement)
expect(renderer.blendMode).toBe('normal')
// Switch to additive
renderer.setBlendMode('additive')
expect(renderer.blendMode).toBe('additive')
expect(blendSrc).toBe(gl.SRC_ALPHA)
expect(blendDst).toBe(gl.ONE)
// Switch back to normal
renderer.setBlendMode('normal')
expect(renderer.blendMode).toBe('normal')
expect(blendSrc).toBe(gl.SRC_ALPHA)
expect(blendDst).toBe(gl.ONE_MINUS_SRC_ALPHA)
// begin() resets blend mode to 'normal'
renderer.setBlendMode('additive')
const camera = { x: 0, y: 0, zoom: 1 }
renderer.begin(camera)
expect(renderer.blendMode).toBe('normal')
})
it('8. drawCastOverlay executes procedural fallback and sprite rendering with anchor offsets', () => {
const draws: any[] = []
const solids: any[] = []
let blendMode: string = 'normal'
const rendererMock = {
blendMode: 'normal',
setBlendMode: (mode: 'normal' | 'additive') => {
blendMode = mode
},
draw: (frame: any, x: number, y: number, options: any) => {
draws.push({ frame, x, y, options })
},
drawSolid: (x: number, y: number, w: number, h: number, color: any, options: any) => {
solids.push({ x, y, w, h, color, options })
},
}
const overlay = createActiveOverlay('fire_cast_1', { x: 300, y: 400 })
// Test procedural fallback (no overlayArt)
drawCastOverlay(rendererMock as any, overlay, undefined)
expect(solids.length).toBeGreaterThan(0)
// Halos drawn with additive blending
expect(solids[0]!.options?.blendMode).toBe('additive')
// Reset to normal
expect(blendMode).toBe('normal')
// Test with loaded overlay art
draws.length = 0
solids.length = 0
const dummyArt: LoadedOverlayArt = {
meta: FIRE_CAST_1_META,
handle: 1 as unknown as AtlasHandle,
frames: [
FIRE_CAST_1_META.groups[0]!.map(([x, y, width, height, anchorX, anchorY]) => ({
x,
y,
width,
height,
anchorX,
anchorY,
})),
],
}
drawCastOverlay(rendererMock as any, overlay, dummyArt)
expect(draws.length).toBe(1)
const drawCall = draws[0]!
expect(drawCall.options.blendMode).toBe('additive')
// Check anchor alignment: drawX = overlay.x + anchorX, drawY = overlay.y + anchorY
const firstFrame = dummyArt.frames[0]![0]!
expect(drawCall.x).toBe(overlay.x + firstFrame.anchorX!)
expect(drawCall.y).toBe(overlay.y + firstFrame.anchorY!)
expect(blendMode).toBe('normal')
})
it('9. Verifies offline baked authentic cast overlay assets and metadata', () => {
const requiredAssets = [
'fire_cast_1',
'fire_cast_2',
'ice_cast_1',
'ice_cast_2',
'ice_cast_3',
'light_cast_1',
'light_cast_2',
'teleport',
]
for (const name of requiredAssets) {
// Check in public/overlays/
const publicPng = path.resolve(__dirname, `../public/overlays/${name}.png`)
const publicJson = path.resolve(__dirname, `../public/overlays/${name}.json`)
expect(fs.existsSync(publicPng), `Missing ${publicPng}`).toBe(true)
expect(fs.existsSync(publicJson), `Missing ${publicJson}`).toBe(true)
const meta = JSON.parse(fs.readFileSync(publicJson, 'utf-8'))
expect(meta.name).toBe(name)
expect(meta.width).toBeGreaterThan(0)
expect(meta.height).toBeGreaterThan(0)
expect(meta.groups.length).toBe(1)
expect(meta.groups[0].length).toBeGreaterThan(0)
// Check in samples/d2-packs/overlays/
const packPng = path.resolve(__dirname, `../samples/d2-packs/overlays/${name}.png`)
const packJson = path.resolve(__dirname, `../samples/d2-packs/overlays/${name}.json`)
expect(fs.existsSync(packPng), `Missing ${packPng}`).toBe(true)
expect(fs.existsSync(packJson), `Missing ${packJson}`).toBe(true)
// Check in OVERLAY_METAS export
expect(OVERLAY_METAS[name]).toBeDefined()
}
// Verify Teleport meta
expect(TELEPORT_META.name).toBe('teleport')
expect(TELEPORT_META.celFile).toBe('Teleport')
expect(TELEPORT_META.lightColor).toEqual([255, 255, 200])
expect(TELEPORT_META.lightRadius).toBe(5)
})
it('10. SpriteRenderer.setBlendMode safely early-returns when disposed (F1)', () => {
let blendFuncCalls = 0
const gl: any = {
VERTEX_SHADER: 35633,
FRAGMENT_SHADER: 35632,
COMPILE_STATUS: 35713,
LINK_STATUS: 35714,
MAX_TEXTURE_SIZE: 3379,
MAX_TEXTURE_IMAGE_UNITS: 34930,
TEXTURE_2D: 3553,
RGBA: 6408,
RGBA8: 32856,
UNSIGNED_BYTE: 5121,
UNSIGNED_INT: 5125,
FLOAT: 5126,
ARRAY_BUFFER: 34962,
ELEMENT_ARRAY_BUFFER: 34963,
DYNAMIC_DRAW: 35048,
STATIC_DRAW: 35044,
TRIANGLES: 4,
TEXTURE_MIN_FILTER: 10241,
TEXTURE_MAG_FILTER: 10240,
TEXTURE_WRAP_S: 10242,
TEXTURE_WRAP_T: 10243,
CLAMP_TO_EDGE: 33071,
NEAREST: 9728,
UNPACK_ALIGNMENT: 3317,
BLEND: 3042,
SRC_ALPHA: 770,
ONE: 1,
ONE_MINUS_SRC_ALPHA: 771,
DEPTH_TEST: 2929,
COLOR_BUFFER_BIT: 16384,
TEXTURE0: 33984,
drawingBufferWidth: 800,
drawingBufferHeight: 600,
isContextLost: () => false,
createProgram: () => ({ id: 1, type: 'program' }),
deleteProgram: () => {},
attachShader: () => {},
detachShader: () => {},
linkProgram: () => {},
getProgramParameter: () => true,
getProgramInfoLog: () => '',
useProgram: () => {},
createShader: (type: number) => ({ id: 1, type }),
shaderSource: () => {},
compileShader: () => {},
getShaderParameter: () => true,
getShaderInfoLog: () => '',
deleteShader: () => {},
createVertexArray: () => ({ id: 1, type: 'vao' }),
deleteVertexArray: () => {},
bindVertexArray: () => {},
createBuffer: () => ({ id: 1, type: 'buffer' }),
deleteBuffer: () => {},
bindBuffer: () => {},
bufferData: () => {},
bufferSubData: () => {},
getAttribLocation: () => 0,
enableVertexAttribArray: () => {},
vertexAttribPointer: () => {},
createTexture: () => ({ id: 1, type: 'texture' }),
deleteTexture: () => {},
bindTexture: () => {},
texParameteri: () => {},
pixelStorei: () => {},
texImage2D: () => {},
getParameter: (param: number) => (param === 3379 ? 4096 : param === 34930 ? 16 : 0),
getUniformLocation: (_p: any, name: string) => ({ name }),
uniform2f: () => {},
uniform1f: () => {},
uniform1i: () => {},
uniform1iv: () => {},
enable: () => {},
disable: () => {},
blendFunc: () => {
blendFuncCalls++
},
viewport: () => {},
clearColor: () => {},
clear: () => {},
activeTexture: () => {},
drawArrays: () => {},
drawElements: () => {},
}
const canvas: any = {
width: 800,
height: 600,
clientWidth: 800,
clientHeight: 600,
getContext: (type: string) => (type === 'webgl2' ? gl : null),
addEventListener: () => {},
removeEventListener: () => {},
}
const renderer = new SpriteRenderer(canvas as HTMLCanvasElement)
renderer.setBlendMode('additive')
const callsBeforeDispose = blendFuncCalls
expect(callsBeforeDispose).toBeGreaterThan(0)
// Dispose renderer
renderer.dispose()
expect(renderer.isDisposed).toBe(true)
// Attempting to setBlendMode after dispose must safely return early
renderer.setBlendMode('normal')
expect(blendFuncCalls).toBe(callsBeforeDispose)
})
it('11. loadOverlayArtMap safely returns empty map in headless/Node without createImageBitmap (F2)', async () => {
const dummyRenderer: any = {}
// In Node.js environment, createImageBitmap is undefined by default
expect(typeof createImageBitmap).toBe('undefined')
const artMap = await loadOverlayArtMap(dummyRenderer)
expect(artMap.size).toBe(0)
})
it('12. ice_cast_3 documented and handles 15 (table) vs 16 (DCC) frame discrepancy (F3)', () => {
// Overlay.txt table lists 15 frames
const overlayMap = parseOverlayTxt(overlayTxtContent)
const tableIceCast3 = overlayMap.get('ice_cast_3')
expect(tableIceCast3?.frames).toBe(15)
// CANONICAL_113C_OVERLAYS uses 16 frames matching DCC asset IceCastNew03.dcc
expect(CANONICAL_113C_OVERLAYS['ice_cast_3']?.frames).toBe(16)
expect(OVERLAY_METAS['ice_cast_3']?.frames).toBe(16)
})
it('13. Rapid consecutive casting replaces active overlay for same casterId to prevent oversaturation (F4)', () => {
const terrain = { widthPx: 1000, heightPx: 1000, overlap: () => 0 }
const engine = new GameEngine(terrain, {
spawn: { x: 200, y: 200 },
stats: [],
xpTable: DEMO_EXPERIENCE,
skills: DEMO_SKILLS,
npcDefs: [],
questDefs: DEMO_QUESTS,
combatOptions: {
playerSpeed: 4,
playerReach: 50,
playerCooldownTicks: 0,
playerDamage: 5,
playerManaPerAttack: 1,
respawnTicks: 100,
},
talkRadius: 50,
pickupRadius: 30,
inventoryCols: 10,
inventoryRows: 4,
monsterCount: 0,
})
// Spawn initial overlay for player
const ov1 = engine.spawnOverlay('fire_cast_1', { x: 200, y: 200 }, 'player')
expect(engine.overlays.length).toBe(1)
expect(engine.overlays[0]).toBe(ov1)
expect(ov1.expired).toBe(false)
// Rapid consecutive cast replaces player's active overlay
const ov2 = engine.spawnOverlay('fire_cast_2', { x: 200, y: 200 }, 'player')
expect(engine.overlays.length).toBe(1)
expect(engine.overlays[0]).toBe(ov2)
expect(ov1.expired).toBe(true)
expect(ov2.expired).toBe(false)
// Overlays for different casterId (e.g. minion or monster) coexist independently
const monsterOv = engine.spawnOverlay('ice_cast_1', { x: 300, y: 300 }, 'monster_1')
expect(engine.overlays.length).toBe(2)
expect(engine.overlays).toContain(ov2)
expect(engine.overlays).toContain(monsterOv)
// A 3rd cast by player replaces ov2, but leaves monsterOv intact
const ov3 = engine.spawnOverlay('teleport', { x: 250, y: 250 }, 'player')
expect(engine.overlays.length).toBe(2)
expect(ov2.expired).toBe(true)
expect(monsterOv.expired).toBe(false)
expect(engine.overlays).toContain(ov3)
expect(engine.overlays).toContain(monsterOv)
})
})