@v12-engine/create 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -2
  3. package/base/_gitignore +10 -0
  4. package/base/index.html +20 -0
  5. package/base/package.json +23 -0
  6. package/base/src/ground.worker.ts +8 -0
  7. package/base/src/main.ts +61 -0
  8. package/base/tsconfig.json +14 -0
  9. package/base/vite.config.ts +25 -0
  10. package/bin.mjs +10 -0
  11. package/cli.mjs +381 -0
  12. package/index.mjs +286 -0
  13. package/layers/assets/package.json +5 -0
  14. package/layers/editor/package.json +8 -0
  15. package/layers/editor/src/editor.ts +13 -0
  16. package/layers/rooms/server.ts +69 -0
  17. package/layers/server/package.json +8 -0
  18. package/layers/server/server.ts +34 -0
  19. package/layers/source/package.json +12 -0
  20. package/layers/source/tsconfig.json +5 -0
  21. package/layers/source/vite.config.ts +45 -0
  22. package/layers/tests/package.json +8 -0
  23. package/package.json +45 -5
  24. package/starters/campaign/README.md +61 -0
  25. package/starters/campaign/files.txt +42 -0
  26. package/starters/campaign/layers/editor/src/editor.ts +18 -0
  27. package/starters/campaign/layers/rooms/server.ts +39 -0
  28. package/starters/campaign/layers/server/server.ts +33 -0
  29. package/starters/campaign/layers/tests/tests/rules.test.ts +320 -0
  30. package/starters/campaign/public/games/campaign/assets.json +1 -0
  31. package/starters/campaign/src/campaign/campaign.json +52 -0
  32. package/starters/campaign/src/campaign/carry.ts +47 -0
  33. package/starters/campaign/src/campaign/cinematics/intro.json +148 -0
  34. package/starters/campaign/src/campaign/cinematics/outro.json +92 -0
  35. package/starters/campaign/src/campaign/data.json +1 -0
  36. package/starters/campaign/src/campaign/fx.json +1 -0
  37. package/starters/campaign/src/campaign/game.ts +6 -0
  38. package/starters/campaign/src/campaign/ids.ts +77 -0
  39. package/starters/campaign/src/campaign/index.ts +121 -0
  40. package/starters/campaign/src/campaign/kills.ts +16 -0
  41. package/starters/campaign/src/campaign/looks.json +1 -0
  42. package/starters/campaign/src/campaign/maps.ts +34 -0
  43. package/starters/campaign/src/campaign/missions/homecoming.ts +160 -0
  44. package/starters/campaign/src/campaign/missions/siege.ts +91 -0
  45. package/starters/campaign/src/campaign/missions/warlord.ts +177 -0
  46. package/starters/campaign/src/campaign/prefabs/watchpost.prefab.json +18 -0
  47. package/starters/campaign/src/campaign/scenes.ts +46 -0
  48. package/starters/campaign/src/campaign/tiles.ts +8 -0
  49. package/starters/campaign/src/campaign/units.ts +390 -0
  50. package/starters/campaign/src/campaign/view/cinematics.ts +59 -0
  51. package/starters/campaign/src/campaign/view/fire.ts +115 -0
  52. package/starters/campaign/src/campaign/view/index.ts +334 -0
  53. package/starters/campaign/src/campaign/view/labels.ts +105 -0
  54. package/starters/campaign/src/campaign/view/rubble.ts +40 -0
  55. package/starters/campaign/src/campaign/view/waveLane.ts +88 -0
  56. package/starters/campaign/src/campaign/waves.json +36 -0
  57. package/starters/campaign/src/campaign/waves.ts +186 -0
  58. package/starters/campaign/src/campaign-editor.ts +43 -0
  59. package/starters/campaign/src/game.ts +2 -0
  60. package/starters/campaign/src/main.ts +70 -0
  61. package/starters/campaign/src/maps/fort.names.ts +31 -0
  62. package/starters/campaign/src/maps/fort.vgmap.json +1162 -0
  63. package/starters/campaign/src/maps/homestead.names.ts +36 -0
  64. package/starters/campaign/src/maps/homestead.vgmap.json +916 -0
  65. package/starters/campaign/src/maps/pass.names.ts +40 -0
  66. package/starters/campaign/src/maps/pass.vgmap.json +522 -0
  67. package/starters/campaign/src/maps.ts +2 -0
  68. package/starters/campaign/src/rules.ts +20 -0
  69. package/starters/campaign/src/waves-panel.ts +355 -0
  70. package/starters/empty/README.md +71 -0
  71. package/starters/empty/layers/tests/tests/rules.test.ts +31 -0
  72. package/starters/empty/src/game.ts +79 -0
  73. package/starters/empty/src/generate.worker.ts +9 -0
  74. package/starters/empty/src/maps.ts +44 -0
  75. package/starters/empty/src/rules.ts +67 -0
  76. package/starters/empty/src/tiles.ts +12 -0
  77. package/starters/melee/README.md +61 -0
  78. package/starters/melee/files.txt +60 -0
  79. package/starters/melee/layers/editor/src/editor.ts +13 -0
  80. package/starters/melee/layers/tests/tests/rules.test.ts +58 -0
  81. package/starters/melee/src/game.ts +2 -0
  82. package/starters/melee/src/maps/bay.names.ts +28 -0
  83. package/starters/melee/src/maps/bay.vgmap.json +3123 -0
  84. package/starters/melee/src/maps.ts +20 -0
  85. package/starters/melee/src/melee/abilities.ts +376 -0
  86. package/starters/melee/src/melee/bay.ts +319 -0
  87. package/starters/melee/src/melee/blight.ts +102 -0
  88. package/starters/melee/src/melee/cellLayers.json +11 -0
  89. package/starters/melee/src/melee/data.json +18 -0
  90. package/starters/melee/src/melee/floats.json +199 -0
  91. package/starters/melee/src/melee/formations.ts +46 -0
  92. package/starters/melee/src/melee/fx.json +1 -0
  93. package/starters/melee/src/melee/game.ts +6 -0
  94. package/starters/melee/src/melee/generator.ts +116 -0
  95. package/starters/melee/src/melee/heroSpells.ts +38 -0
  96. package/starters/melee/src/melee/hud.json +269 -0
  97. package/starters/melee/src/melee/index.ts +182 -0
  98. package/starters/melee/src/melee/items.ts +362 -0
  99. package/starters/melee/src/melee/languages/he.json +96 -0
  100. package/starters/melee/src/melee/looks.json +1 -0
  101. package/starters/melee/src/melee/maps.ts +53 -0
  102. package/starters/melee/src/melee/pings.json +16 -0
  103. package/starters/melee/src/melee/pings.ts +34 -0
  104. package/starters/melee/src/melee/quality.json +1 -0
  105. package/starters/melee/src/melee/saves.ts +12 -0
  106. package/starters/melee/src/melee/sounds.json +1 -0
  107. package/starters/melee/src/melee/undead.ts +683 -0
  108. package/starters/melee/src/melee/units.ts +785 -0
  109. package/starters/melee/src/melee/view/buildings.ts +33 -0
  110. package/starters/melee/src/melee/view/cheerLane.ts +42 -0
  111. package/starters/melee/src/melee/view/heroSpellLooks.ts +21 -0
  112. package/starters/melee/src/melee/view/index.ts +268 -0
  113. package/starters/melee/src/melee/view/languages.ts +10 -0
  114. package/starters/melee/src/melee/view/looks.ts +659 -0
  115. package/starters/melee/src/melee/view/undeadLooks.ts +220 -0
  116. package/starters/melee/src/melee/view/walls.ts +59 -0
  117. package/starters/melee/src/rules.ts +24 -0
  118. package/starters.mjs +35 -0
  119. package/v12.mjs +111 -0
@@ -0,0 +1,683 @@
1
+ import type { AbilityDef, BuffDef, UnitTypeDef, UpgradeDef } from '@v12-engine/engine'
2
+ import { catalogue } from './items'
3
+
4
+ /*
5
+ * The Restless: melee's second race (features-plan C4), on the KayKit skeletons (minion, rogue, warrior) and their
6
+ * axe, blade, crossbow, shield and staff. Everything here is of the race `undead`: only its players make it
7
+ */
8
+
9
+ const RACE = 'undead'
10
+
11
+ // ---- units
12
+
13
+ export const acolyte: UnitTypeDef = {
14
+ id: 'acolyte',
15
+ armourType: 'medium',
16
+ race: RACE,
17
+ name: 'Acolyte',
18
+ kind: 'unit',
19
+ radius: 0.4,
20
+ stats: { health: 220, attackDamage: 8, attackSpeed: 0.6, attackRange: 0.4, moveSpeed: 4.05 },
21
+ sight: 8,
22
+ attacks: [{ attackType: 'normal', damagePoint: 0.4, acquisitionRange: 0, targets: ['unit', 'building'] }],
23
+ cost: { gold: 75 },
24
+ supply: 1,
25
+ buildTime: 10,
26
+ // gold, and only at a cursed mine: round it, handed in on the spot (`undeadHooks`)
27
+ harvester: { capacity: 10, gatherTime: 2, kinds: ['gold'] },
28
+ builds: ['gravehold', 'cursedMine', 'boneSpire', 'crypt', 'graveyard', 'darkAltar', 'slaughterhouse', 'temple', 'spiritTower', 'tomb'],
29
+ hotkey: 'C',
30
+ threat: 1
31
+ }
32
+
33
+ export const ghoul: UnitTypeDef = {
34
+ id: 'ghoul',
35
+ armourType: 'medium',
36
+ race: RACE,
37
+ name: 'Ghoul',
38
+ kind: 'unit',
39
+ radius: 0.4,
40
+ // fast and cheap, in numbers
41
+ stats: { health: 340, attackDamage: 13, attackSpeed: 1.4, attackRange: 0.5, armor: 0, moveSpeed: 4.6 },
42
+ sight: 10,
43
+ attacks: [{ attackType: 'normal', damagePoint: 0.35, acquisitionRange: 8 }],
44
+ // the Restless' lumber: a ghoul carries twice what a peasant does
45
+ harvester: { capacity: 20, gatherTime: 1.5, kinds: ['lumber'] },
46
+ // to the computer player, both at once: it chops, and it fights (what the kit would infer too; said, so that a
47
+ // change to the kit's rules never makes it one or the other)
48
+ ai: { role: ['gatherer', 'soldier'] },
49
+ cost: { gold: 120 },
50
+ supply: 2,
51
+ buildTime: 13,
52
+ hotkey: 'G'
53
+ }
54
+
55
+ export const cryptCrawler: UnitTypeDef = {
56
+ id: 'cryptCrawler',
57
+ armourType: 'medium',
58
+ race: RACE,
59
+ name: 'Crypt Crawler',
60
+ kind: 'unit',
61
+ radius: 0.45,
62
+ stats: { health: 550, attackDamage: 26, attackSpeed: 0.5, attackRange: 5.5, armor: 5, moveSpeed: 3.8 },
63
+ sight: 11,
64
+ attacks: [{ attackType: 'pierce', damagePoint: 0.35, acquisitionRange: 8, projectileSpeed: 20, targets: ['ground', 'air'] }],
65
+ abilities: ['web'],
66
+ cost: { gold: 215, lumber: 40 },
67
+ supply: 3,
68
+ buildTime: 20,
69
+ requires: ['graveyard'],
70
+ hotkey: 'F'
71
+ }
72
+
73
+ export const abomination: UnitTypeDef = {
74
+ id: 'abomination',
75
+ armourType: 'heavy',
76
+ race: RACE,
77
+ name: 'Abomination',
78
+ kind: 'unit',
79
+ radius: 0.65,
80
+ stats: { health: 1080, healthRegen: 1, attackDamage: 34, attackSpeed: 0.55, attackRange: 0.6, armor: 20, moveSpeed: 3.6 },
81
+ sight: 10,
82
+ attacks: [{ attackType: 'normal', damagePoint: 0.5, acquisitionRange: 8 }],
83
+ cost: { gold: 240, lumber: 70 },
84
+ supply: 4,
85
+ buildTime: 30,
86
+ requires: ['hallOfBones'],
87
+ hotkey: 'A'
88
+ }
89
+
90
+ export const necromancer: UnitTypeDef = {
91
+ id: 'necromancer',
92
+ armourType: 'light',
93
+ race: RACE,
94
+ name: 'Necromancer',
95
+ kind: 'unit',
96
+ radius: 0.4,
97
+ stats: { health: 280, mana: 200, manaRegen: 0.7, attackDamage: 11, attackSpeed: 0.6, attackRange: 6, moveSpeed: 4.05 },
98
+ initialMana: 75,
99
+ sight: 10,
100
+ attacks: [{ attackType: 'magic', damageType: 'magic', damagePoint: 0.4, acquisitionRange: 7, projectileSpeed: 16, targets: ['ground', 'air'] }],
101
+ abilities: ['raiseDead', 'unholyFrenzy', 'cripple'],
102
+ cost: { gold: 145, lumber: 10 },
103
+ supply: 2,
104
+ buildTime: 18,
105
+ requires: ['hallOfBones'],
106
+ hotkey: 'N'
107
+ }
108
+
109
+ export const gargoyle: UnitTypeDef = {
110
+ id: 'gargoyle',
111
+ armourType: 'light',
112
+ race: RACE,
113
+ name: 'Gargoyle',
114
+ kind: 'unit',
115
+ radius: 0.55,
116
+ flying: true,
117
+ stats: { health: 450, attackDamage: 30, attackSpeed: 0.6, attackRange: 3.5, armor: 15, magicResist: 15, moveSpeed: 5 },
118
+ sight: 12,
119
+ turnRate: 6,
120
+ attacks: [{ attackType: 'pierce', damagePoint: 0.4, acquisitionRange: 8, projectileSpeed: 16, targets: ['ground', 'air'] }],
121
+ cost: { gold: 185, lumber: 30 },
122
+ supply: 2,
123
+ buildTime: 25,
124
+ requires: ['hallOfBones', 'graveyard'],
125
+ hotkey: 'R'
126
+ }
127
+
128
+ // ---- heroes
129
+
130
+ export const deathKnight: UnitTypeDef = {
131
+ id: 'deathKnight',
132
+ armourType: 'hero',
133
+ race: RACE,
134
+ name: 'Death Knight',
135
+ kind: 'unit',
136
+ radius: 0.5,
137
+ stats: {
138
+ health: 640,
139
+ healthRegen: 1.2,
140
+ mana: 280,
141
+ manaRegen: 1.6,
142
+ attackDamage: 54,
143
+ attackSpeed: 0.65,
144
+ attackRange: 0.6,
145
+ armor: 38,
146
+ magicResist: 32,
147
+ moveSpeed: 4.4
148
+ },
149
+ sight: 12,
150
+ attacks: [{ attackType: 'hero', damagePoint: 0.45, acquisitionRange: 8 }],
151
+ hero: {
152
+ growth: {
153
+ stats: { health: 96, healthRegen: 0.1, mana: 55, manaRegen: 0.2, attackDamage: 3.6, armor: 4.1, magicResist: 2.05 },
154
+ percent: { attackSpeed: 0.02 }
155
+ },
156
+ learns: ['deathCoil', 'unholyAura', 'darkCharge', 'animateDead'],
157
+ spells: ['barrier', 'flash']
158
+ },
159
+ inventory: 6,
160
+ cost: { gold: 425, lumber: 100 },
161
+ supply: 5,
162
+ buildTime: 45,
163
+ hotkey: 'D',
164
+ threat: 3
165
+ }
166
+
167
+ export const lich: UnitTypeDef = {
168
+ id: 'lich',
169
+ armourType: 'hero',
170
+ race: RACE,
171
+ name: 'Lich',
172
+ kind: 'unit',
173
+ radius: 0.5,
174
+ stats: {
175
+ health: 560,
176
+ healthRegen: 0.9,
177
+ mana: 400,
178
+ manaRegen: 2,
179
+ attackDamage: 44,
180
+ attackSpeed: 0.6,
181
+ attackRange: 6,
182
+ armor: 24,
183
+ magicResist: 30,
184
+ moveSpeed: 4.1
185
+ },
186
+ sight: 12,
187
+ attacks: [{ attackType: 'hero', damageType: 'magic', damagePoint: 0.4, acquisitionRange: 8, projectileSpeed: 18, targets: ['ground', 'air'] }],
188
+ hero: {
189
+ growth: {
190
+ stats: { health: 80, healthRegen: 0.08, mana: 75, manaRegen: 0.25, attackDamage: 3, armor: 3.5, magicResist: 2 },
191
+ percent: { attackSpeed: 0.015 }
192
+ },
193
+ learns: ['frostNova', 'frostArmor', 'darkAura', 'deathAndDecay'],
194
+ spells: ['barrier', 'flash']
195
+ },
196
+ inventory: 6,
197
+ cost: { gold: 425, lumber: 100 },
198
+ supply: 5,
199
+ buildTime: 45,
200
+ hotkey: 'L',
201
+ threat: 3
202
+ }
203
+
204
+ // ---- buildings
205
+
206
+ // gold is handed in at the cursed mine: a hall takes the lumber
207
+ const HALL: Pick<UnitTypeDef, 'kind' | 'radius' | 'footprint' | 'sight' | 'supplyProvided' | 'trains' | 'dropOff' | 'buildsItself'> = {
208
+ kind: 'building',
209
+ radius: 2,
210
+ footprint: { w: 4, d: 4 },
211
+ sight: 12,
212
+ supplyProvided: 12,
213
+ trains: ['acolyte'],
214
+ dropOff: ['lumber'],
215
+ buildsItself: true
216
+ }
217
+
218
+ export const gravehold: UnitTypeDef = {
219
+ ...HALL,
220
+ id: 'gravehold',
221
+ armourType: 'fortified',
222
+ race: RACE,
223
+ name: 'Gravehold',
224
+ trains: ['acolyte'],
225
+ stats: { health: 2100, armor: 40, magicResist: 40 },
226
+ cost: { gold: 385, lumber: 185 },
227
+ buildTime: 45,
228
+ upgradesTo: 'hallOfBones',
229
+ hotkey: 'N'
230
+ }
231
+
232
+ export const hallOfBones: UnitTypeDef = {
233
+ ...HALL,
234
+ id: 'hallOfBones',
235
+ armourType: 'fortified',
236
+ race: RACE,
237
+ name: 'Hall of Bones',
238
+ trains: ['acolyte'],
239
+ stats: { health: 2850, armor: 40, magicResist: 40 },
240
+ cost: { gold: 320, lumber: 210 },
241
+ buildTime: 50,
242
+ provides: ['gravehold'],
243
+ upgradesTo: 'duskCitadel',
244
+ hotkey: 'U'
245
+ }
246
+
247
+ export const duskCitadel: UnitTypeDef = {
248
+ ...HALL,
249
+ id: 'duskCitadel',
250
+ armourType: 'fortified',
251
+ race: RACE,
252
+ name: 'Dusk Citadel',
253
+ trains: ['acolyte'],
254
+ stats: { health: 3600, armor: 40, magicResist: 40 },
255
+ cost: { gold: 360, lumber: 210 },
256
+ buildTime: 60,
257
+ provides: ['gravehold', 'hallOfBones'],
258
+ hotkey: 'U'
259
+ }
260
+
261
+ export const cursedMine: UnitTypeDef = {
262
+ id: 'cursedMine',
263
+ armourType: 'fortified',
264
+ race: RACE,
265
+ buildsItself: true,
266
+ name: 'Cursed Mine',
267
+ kind: 'building',
268
+ radius: 2,
269
+ // over a gold mine, its very ground: the mine is the Restless', its acolytes gather round it
270
+ footprint: { w: 4, d: 4 },
271
+ builtOn: 'gold',
272
+ dropOff: ['gold'],
273
+ gatherers: 5,
274
+ stats: { health: 800, armor: 40, magicResist: 40 },
275
+ sight: 9,
276
+ cost: { gold: 255, lumber: 120 },
277
+ buildTime: 30,
278
+ hotkey: 'H'
279
+ }
280
+
281
+ export const boneSpire: UnitTypeDef = {
282
+ id: 'boneSpire',
283
+ armourType: 'fortified',
284
+ race: RACE,
285
+ buildsItself: true,
286
+ name: 'Bone Spire',
287
+ kind: 'building',
288
+ radius: 1,
289
+ footprint: { w: 2, d: 2 },
290
+ stats: { health: 650, armor: 40, magicResist: 40 },
291
+ sight: 8,
292
+ cost: { gold: 150 },
293
+ buildTime: 15,
294
+ // more food than a farm, for more gold and no lumber
295
+ supplyProvided: 8,
296
+ hotkey: 'Z'
297
+ }
298
+
299
+ export const crypt: UnitTypeDef = {
300
+ id: 'crypt',
301
+ armourType: 'fortified',
302
+ race: RACE,
303
+ buildsItself: true,
304
+ name: 'Crypt',
305
+ kind: 'building',
306
+ radius: 1.5,
307
+ footprint: { w: 3, d: 3 },
308
+ stats: { health: 1300, armor: 40, magicResist: 40 },
309
+ sight: 9,
310
+ cost: { gold: 200, lumber: 50 },
311
+ buildTime: 25,
312
+ trains: ['ghoul', 'cryptCrawler'],
313
+ hotkey: 'C'
314
+ }
315
+
316
+ export const graveyard: UnitTypeDef = {
317
+ id: 'graveyard',
318
+ armourType: 'fortified',
319
+ race: RACE,
320
+ buildsItself: true,
321
+ name: 'Graveyard',
322
+ kind: 'building',
323
+ radius: 1.5,
324
+ footprint: { w: 3, d: 3 },
325
+ stats: { health: 1200, armor: 40, magicResist: 40 },
326
+ sight: 9,
327
+ cost: { gold: 215 },
328
+ buildTime: 25,
329
+ requires: ['crypt'],
330
+ // where lumber is brought, and where the dead are made stronger
331
+ dropOff: ['lumber'],
332
+ researches: ['unholyStrength', 'creatureAttack', 'unholyArmor'],
333
+ hotkey: 'G'
334
+ }
335
+
336
+ export const darkAltar: UnitTypeDef = {
337
+ id: 'darkAltar',
338
+ armourType: 'fortified',
339
+ race: RACE,
340
+ buildsItself: true,
341
+ name: 'Dark Altar',
342
+ kind: 'building',
343
+ radius: 1.5,
344
+ footprint: { w: 3, d: 3 },
345
+ stats: { health: 1350, armor: 40, magicResist: 40 },
346
+ sight: 9,
347
+ cost: { gold: 180, lumber: 50 },
348
+ buildTime: 30,
349
+ trains: ['deathKnight', 'lich'],
350
+ revives: true,
351
+ hotkey: 'A'
352
+ }
353
+
354
+ export const slaughterhouse: UnitTypeDef = {
355
+ id: 'slaughterhouse',
356
+ armourType: 'fortified',
357
+ race: RACE,
358
+ buildsItself: true,
359
+ name: 'Slaughterhouse',
360
+ kind: 'building',
361
+ radius: 1.5,
362
+ footprint: { w: 3, d: 3 },
363
+ stats: { health: 1200, armor: 40, magicResist: 40 },
364
+ sight: 9,
365
+ cost: { gold: 140, lumber: 135 },
366
+ buildTime: 30,
367
+ requires: ['hallOfBones', 'graveyard'],
368
+ trains: ['abomination', 'gargoyle'],
369
+ hotkey: 'S'
370
+ }
371
+
372
+ export const temple: UnitTypeDef = {
373
+ id: 'temple',
374
+ armourType: 'fortified',
375
+ race: RACE,
376
+ buildsItself: true,
377
+ name: 'Temple of Shades',
378
+ kind: 'building',
379
+ radius: 1.5,
380
+ footprint: { w: 3, d: 3 },
381
+ stats: { health: 1250, armor: 40, magicResist: 40 },
382
+ sight: 9,
383
+ cost: { gold: 155, lumber: 140 },
384
+ buildTime: 30,
385
+ requires: ['hallOfBones'],
386
+ trains: ['necromancer'],
387
+ hotkey: 'T'
388
+ }
389
+
390
+ export const spiritTower: UnitTypeDef = {
391
+ id: 'spiritTower',
392
+ armourType: 'fortified',
393
+ race: RACE,
394
+ buildsItself: true,
395
+ name: 'Spirit Tower',
396
+ kind: 'building',
397
+ radius: 1,
398
+ footprint: { w: 2, d: 2 },
399
+ stats: { health: 1300, attackDamage: 55, attackSpeed: 0.9, attackRange: 8, armor: 40, magicResist: 40 },
400
+ sight: 12,
401
+ attacks: [{ attackType: 'magic', damageType: 'magic', damagePoint: 0.3, acquisitionRange: 8, projectileSpeed: 22, targets: ['ground', 'air'] }],
402
+ cost: { gold: 145, lumber: 40 },
403
+ buildTime: 20,
404
+ requires: ['crypt'],
405
+ hotkey: 'W'
406
+ }
407
+
408
+ export const tomb: UnitTypeDef = {
409
+ id: 'tomb',
410
+ armourType: 'fortified',
411
+ race: RACE,
412
+ buildsItself: true,
413
+ name: 'Reliquary',
414
+ kind: 'building',
415
+ radius: 1.5,
416
+ footprint: { w: 3, d: 3 },
417
+ stats: { health: 1300, armor: 40, magicResist: 40 },
418
+ sight: 9,
419
+ cost: { gold: 130, lumber: 30 },
420
+ buildTime: 22,
421
+ requires: ['hallOfBones'],
422
+ sells: catalogue,
423
+ hotkey: 'O'
424
+ }
425
+
426
+ export const undeadUnits: UnitTypeDef[] = [
427
+ acolyte,
428
+ ghoul,
429
+ cryptCrawler,
430
+ abomination,
431
+ necromancer,
432
+ gargoyle,
433
+ deathKnight,
434
+ lich,
435
+ gravehold,
436
+ hallOfBones,
437
+ duskCitadel,
438
+ cursedMine,
439
+ boneSpire,
440
+ crypt,
441
+ graveyard,
442
+ darkAltar,
443
+ slaughterhouse,
444
+ temple,
445
+ spiritTower,
446
+ tomb
447
+ ]
448
+
449
+ // ---- research
450
+
451
+ /** The second level asks for the Hall of Bones, the third for the Dusk Citadel */
452
+ const LEVELS = [[], ['hallOfBones'], ['duskCitadel']]
453
+
454
+ export const undeadUpgrades: UpgradeDef[] = [
455
+ {
456
+ id: 'unholyStrength',
457
+ race: RACE,
458
+ requires: LEVELS,
459
+ name: 'Unholy Strength',
460
+ description: '+2 damage for ghouls and abominations',
461
+ cost: { gold: 100, lumber: 50 },
462
+ researchTime: 20,
463
+ maxLevel: 3,
464
+ hotkey: 'S',
465
+ effects: [{ units: ['ghoul', 'abomination'], stats: { attackDamage: 2 } }]
466
+ },
467
+ {
468
+ id: 'creatureAttack',
469
+ race: RACE,
470
+ requires: LEVELS,
471
+ name: 'Creature Attack',
472
+ description: '+3 damage for crypt crawlers and gargoyles',
473
+ cost: { gold: 120, lumber: 50 },
474
+ researchTime: 20,
475
+ maxLevel: 3,
476
+ hotkey: 'C',
477
+ effects: [{ units: ['cryptCrawler', 'gargoyle'], stats: { attackDamage: 3 } }]
478
+ },
479
+ {
480
+ id: 'unholyArmor',
481
+ race: RACE,
482
+ requires: LEVELS,
483
+ name: 'Unholy Armor',
484
+ description: '+12 armor for ghouls, abominations and crypt crawlers',
485
+ cost: { gold: 125, lumber: 75 },
486
+ researchTime: 25,
487
+ maxLevel: 3,
488
+ hotkey: 'A',
489
+ effects: [{ units: ['ghoul', 'abomination', 'cryptCrawler'], stats: { armor: 12 } }]
490
+ }
491
+ ]
492
+
493
+ // ---- what they do
494
+
495
+ export const undeadBuffs: BuffDef[] = [
496
+ {
497
+ id: 'unholy',
498
+ name: 'Unholy Aura',
499
+ description: 'Moves faster and heals near a Death Knight',
500
+ percent: { moveSpeed: [0.1, 0.15, 0.2] },
501
+ stats: { healthRegen: [1, 2, 3] }
502
+ },
503
+ { id: 'dark', name: 'Dark Aura', description: 'Mana comes back faster near a Lich', stats: { manaRegen: [0.75, 1.5, 2.25] } },
504
+ {
505
+ id: 'darkCharge',
506
+ name: 'Dark Charge',
507
+ description: 'Rides hard: faster, and harder to hurt',
508
+ duration: 6,
509
+ percent: { moveSpeed: 0.4 },
510
+ stats: { armor: [10, 20, 30] }
511
+ },
512
+ { id: 'frostArmor', name: 'Frost Armor', description: 'Ice over it: more armor', duration: 30, stats: { armor: [15, 25, 35] } },
513
+ {
514
+ id: 'chilled',
515
+ name: 'Chilled',
516
+ description: 'Moves and attacks slower',
517
+ duration: 4,
518
+ harmful: true,
519
+ percent: { moveSpeed: -0.4, attackSpeed: -0.25 }
520
+ },
521
+ {
522
+ id: 'unholyFrenzy',
523
+ name: 'Unholy Frenzy',
524
+ description: 'Attacks 50% faster, and loses 4 health a second',
525
+ duration: 20,
526
+ percent: { attackSpeed: 0.5 },
527
+ stats: { healthRegen: -4 }
528
+ },
529
+ {
530
+ id: 'crippled',
531
+ name: 'Cripple',
532
+ description: 'Moves 50% and attacks 35% slower, and hits 25% softer',
533
+ duration: 20,
534
+ harmful: true,
535
+ percent: { moveSpeed: -0.5, attackSpeed: -0.35, attackDamage: -0.25 }
536
+ },
537
+ {
538
+ id: 'webbed',
539
+ name: 'Web',
540
+ description: 'Pulled down and stuck: cannot move, and can be hit from the ground',
541
+ harmful: true,
542
+ root: true,
543
+ grounds: true
544
+ }
545
+ ]
546
+
547
+ export const undeadAbilities: AbilityDef[] = [
548
+ // ---- Death Knight
549
+ {
550
+ id: 'deathCoil',
551
+ name: 'Death Coil',
552
+ description: 'Hits an enemy for 100, 175 or 250 (+70% ability power)',
553
+ hotkey: 'C',
554
+ target: 'unit',
555
+ targets: { enemies: true },
556
+ manaCost: 75,
557
+ cooldown: 6,
558
+ range: 8,
559
+ castTime: 0.3,
560
+ effects: [{ type: 'damage', amount: [100, 175, 250], scaling: { abilityPower: 0.7 } }]
561
+ },
562
+ {
563
+ id: 'unholyAura',
564
+ name: 'Unholy Aura',
565
+ description: 'Friendly units nearby move 10, 15 or 20% faster and heal 1, 2 or 3 a second',
566
+ target: 'passive',
567
+ effects: [{ type: 'aura', buff: 'unholy', radius: 9 }]
568
+ },
569
+ {
570
+ id: 'darkCharge',
571
+ name: 'Dark Charge',
572
+ description: 'For 6 seconds: 40% faster and 10, 20 or 30 more armor',
573
+ hotkey: 'E',
574
+ target: 'none',
575
+ manaCost: 60,
576
+ cooldown: 14,
577
+ effects: [{ type: 'buff', buff: 'darkCharge', on: 'caster' }]
578
+ },
579
+ {
580
+ id: 'animateDead',
581
+ name: 'Animate Dead',
582
+ description: 'Raises six skeletons from the bodies around the hero, for 40 seconds',
583
+ hotkey: 'R',
584
+ target: 'none',
585
+ manaCost: 175,
586
+ cooldown: 120,
587
+ maxLevel: 1,
588
+ requiresLevel: 6,
589
+ effects: [{ type: 'raiseDead', unit: 'skeleton', count: 6, radius: 9, duration: 40 }]
590
+ },
591
+
592
+ // ---- Lich
593
+ {
594
+ id: 'frostNova',
595
+ name: 'Frost Nova',
596
+ description: 'Hits an enemy and those around it for 70, 120 or 170 (+60% ability power), and chills them for 4 seconds',
597
+ hotkey: 'Q',
598
+ target: 'unit',
599
+ targets: { enemies: true },
600
+ manaCost: 100,
601
+ cooldown: 8,
602
+ range: 8,
603
+ castTime: 0.3,
604
+ effects: [
605
+ { type: 'damage', amount: [70, 120, 170], scaling: { abilityPower: 0.6 }, radius: 2.5 },
606
+ { type: 'buff', buff: 'chilled', radius: 2.5 }
607
+ ]
608
+ },
609
+ {
610
+ id: 'frostArmor',
611
+ name: 'Frost Armor',
612
+ description: 'Covers a friendly unit in ice: 15, 25 or 35 more armor for 30 seconds',
613
+ hotkey: 'W',
614
+ target: 'unit',
615
+ targets: { allies: true, self: true },
616
+ manaCost: 40,
617
+ cooldown: 2,
618
+ range: 8,
619
+ castTime: 0.3,
620
+ effects: [{ type: 'buff', buff: 'frostArmor' }]
621
+ },
622
+ {
623
+ id: 'darkAura',
624
+ name: 'Dark Aura',
625
+ description: 'Friendly units nearby regain 0.75, 1.5 or 2.25 more mana per second',
626
+ target: 'passive',
627
+ effects: [{ type: 'aura', buff: 'dark', radius: 9 }]
628
+ },
629
+ {
630
+ id: 'deathAndDecay',
631
+ name: 'Death and Decay',
632
+ description: 'Rots everything in an area for 300 (+90% ability power)',
633
+ hotkey: 'R',
634
+ target: 'point',
635
+ manaCost: 200,
636
+ cooldown: 90,
637
+ range: 9,
638
+ maxLevel: 1,
639
+ requiresLevel: 6,
640
+ castTime: 0.8,
641
+ effects: [{ type: 'damage', amount: 300, scaling: { abilityPower: 0.9 }, radius: 4.5 }]
642
+ },
643
+
644
+ // ---- the rest
645
+ {
646
+ id: 'unholyFrenzy',
647
+ name: 'Unholy Frenzy',
648
+ description: 'A friendly unit attacks 50% faster for 20 seconds, and loses 4 health a second',
649
+ hotkey: 'U',
650
+ target: 'unit',
651
+ targets: { allies: true, self: false },
652
+ manaCost: 50,
653
+ cooldown: 2,
654
+ range: 7,
655
+ castTime: 0.3,
656
+ effects: [{ type: 'buff', buff: 'unholyFrenzy' }]
657
+ },
658
+ {
659
+ id: 'cripple',
660
+ name: 'Cripple',
661
+ description: 'An enemy moves 50% and attacks 35% slower, and hits 25% softer, for 20 seconds',
662
+ hotkey: 'C',
663
+ target: 'unit',
664
+ targets: { enemies: true },
665
+ manaCost: 125,
666
+ cooldown: 8,
667
+ range: 7,
668
+ castTime: 0.3,
669
+ effects: [{ type: 'buff', buff: 'crippled' }]
670
+ },
671
+ {
672
+ id: 'web',
673
+ name: 'Web',
674
+ description: 'Pulls a flying enemy down and holds it for 4 seconds, where the ground can hit it',
675
+ hotkey: 'W',
676
+ target: 'unit',
677
+ targets: { enemies: true, layers: ['air'] },
678
+ cooldown: 12,
679
+ range: 6,
680
+ castTime: 0.2,
681
+ effects: [{ type: 'buff', buff: 'webbed', duration: 4 }]
682
+ }
683
+ ]