@carmaclouds/core 2.3.1

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 (118) hide show
  1. package/dist/cache/CacheManager.d.ts.map +1 -0
  2. package/dist/cache/CacheManager.js +131 -0
  3. package/dist/cache/CacheManager.js.map +1 -0
  4. package/dist/index.d.ts +18 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +22 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/ir/index.d.ts +11 -0
  9. package/dist/ir/index.d.ts.map +1 -0
  10. package/dist/ir/index.js +9 -0
  11. package/dist/ir/index.js.map +1 -0
  12. package/dist/ir/normalize.d.ts +10 -0
  13. package/dist/ir/normalize.d.ts.map +1 -0
  14. package/dist/ir/normalize.js +207 -0
  15. package/dist/ir/normalize.js.map +1 -0
  16. package/dist/ir/persistence.d.ts +26 -0
  17. package/dist/ir/persistence.d.ts.map +1 -0
  18. package/dist/ir/persistence.js +21 -0
  19. package/dist/ir/persistence.js.map +1 -0
  20. package/dist/ir/sync.d.ts +12 -0
  21. package/dist/ir/sync.d.ts.map +1 -0
  22. package/dist/ir/sync.js +36 -0
  23. package/dist/ir/sync.js.map +1 -0
  24. package/dist/ir/types.d.ts +143 -0
  25. package/dist/ir/types.d.ts.map +1 -0
  26. package/dist/ir/types.js +13 -0
  27. package/dist/ir/types.js.map +1 -0
  28. package/dist/ir/views/dnd5e.d.ts +40 -0
  29. package/dist/ir/views/dnd5e.d.ts.map +1 -0
  30. package/dist/ir/views/dnd5e.js +50 -0
  31. package/dist/ir/views/dnd5e.js.map +1 -0
  32. package/dist/render/character.d.ts +19 -0
  33. package/dist/render/character.d.ts.map +1 -0
  34. package/dist/render/character.js +156 -0
  35. package/dist/render/character.js.map +1 -0
  36. package/dist/render/h.d.ts +27 -0
  37. package/dist/render/h.d.ts.map +1 -0
  38. package/dist/render/h.js +64 -0
  39. package/dist/render/h.js.map +1 -0
  40. package/dist/render/index.d.ts +11 -0
  41. package/dist/render/index.d.ts.map +1 -0
  42. package/dist/render/index.js +8 -0
  43. package/dist/render/index.js.map +1 -0
  44. package/dist/render/mount.d.ts +31 -0
  45. package/dist/render/mount.d.ts.map +1 -0
  46. package/dist/render/mount.js +63 -0
  47. package/dist/render/mount.js.map +1 -0
  48. package/dist/supabase/fields.d.ts.map +1 -0
  49. package/dist/supabase/fields.js +120 -0
  50. package/dist/supabase/fields.js.map +1 -0
  51. package/dist/types/character.d.ts.map +1 -0
  52. package/dist/types/character.js +5 -0
  53. package/dist/types/character.js.map +1 -0
  54. package/package.json +73 -0
  55. package/src/browser.js +51 -0
  56. package/src/cache/CacheManager.ts +174 -0
  57. package/src/common/browser-polyfill.js +319 -0
  58. package/src/common/debug.js +123 -0
  59. package/src/common/html-utils.js +134 -0
  60. package/src/common/theme-manager.js +265 -0
  61. package/src/index.ts +25 -0
  62. package/src/ir/__fixtures__/dnd5e-character.json +75962 -0
  63. package/src/ir/__fixtures__/non-dnd-character.json +14218 -0
  64. package/src/ir/index.ts +10 -0
  65. package/src/ir/normalize.ts +245 -0
  66. package/src/ir/persistence.ts +37 -0
  67. package/src/ir/sync.ts +49 -0
  68. package/src/ir/types.ts +161 -0
  69. package/src/ir/views/dnd5e.ts +94 -0
  70. package/src/lib/indexeddb-cache.js +320 -0
  71. package/src/modules/action-announcements.js +102 -0
  72. package/src/modules/action-display.js +1557 -0
  73. package/src/modules/action-executor.js +860 -0
  74. package/src/modules/action-filters.js +167 -0
  75. package/src/modules/action-options.js +117 -0
  76. package/src/modules/card-creator.js +142 -0
  77. package/src/modules/character-portrait.js +169 -0
  78. package/src/modules/character-trait-popups.js +959 -0
  79. package/src/modules/character-traits.js +814 -0
  80. package/src/modules/class-feature-edge-cases.js +1320 -0
  81. package/src/modules/color-utils.js +69 -0
  82. package/src/modules/combat-maneuver-edge-cases.js +660 -0
  83. package/src/modules/companions-manager.js +178 -0
  84. package/src/modules/concentration-tracker.js +178 -0
  85. package/src/modules/data-manager.js +514 -0
  86. package/src/modules/dice-roller.js +719 -0
  87. package/src/modules/effects-manager.js +743 -0
  88. package/src/modules/feature-modals.js +1264 -0
  89. package/src/modules/formula-resolver.js +444 -0
  90. package/src/modules/gm-mode.js +184 -0
  91. package/src/modules/health-modals.js +399 -0
  92. package/src/modules/hp-management.js +752 -0
  93. package/src/modules/inventory-manager.js +242 -0
  94. package/src/modules/macro-system.js +825 -0
  95. package/src/modules/notification-system.js +92 -0
  96. package/src/modules/racial-feature-edge-cases.js +746 -0
  97. package/src/modules/resource-manager.js +775 -0
  98. package/src/modules/sheet-builder.js +654 -0
  99. package/src/modules/spell-action-modals.js +583 -0
  100. package/src/modules/spell-cards.js +602 -0
  101. package/src/modules/spell-casting.js +723 -0
  102. package/src/modules/spell-display.js +314 -0
  103. package/src/modules/spell-edge-cases.js +509 -0
  104. package/src/modules/spell-macros.js +201 -0
  105. package/src/modules/spell-modals.js +1221 -0
  106. package/src/modules/spell-slots.js +224 -0
  107. package/src/modules/status-bar-bridge.js +101 -0
  108. package/src/modules/ui-utilities.js +284 -0
  109. package/src/modules/warlock-invocations.js +219 -0
  110. package/src/modules/window-management.js +211 -0
  111. package/src/render/character.ts +234 -0
  112. package/src/render/h.ts +74 -0
  113. package/src/render/index.ts +10 -0
  114. package/src/render/mount.ts +94 -0
  115. package/src/supabase/client.js +1383 -0
  116. package/src/supabase/config.js +60 -0
  117. package/src/supabase/fields.ts +129 -0
  118. package/src/types/character.ts +85 -0
@@ -0,0 +1,746 @@
1
+ /**
2
+ * Racial Features Edge Cases Configuration
3
+ *
4
+ * This file contains all the racial features that need special handling
5
+ * beyond standard attack/damage/healing buttons.
6
+ */
7
+
8
+ const RACIAL_FEATURE_EDGE_CASES = {
9
+ // ===== HALFLING FEATURES =====
10
+ 'lucky': {
11
+ type: 'reroll',
12
+ trigger: 'roll_1_on_attack_save_or_check',
13
+ condition: 'self_or_ally_within_30_feet',
14
+ effect: 'reroll_the_die',
15
+ description: 'Reroll 1s on attacks/saves/checks (self or ally within 30ft)'
16
+ },
17
+ 'brave': {
18
+ type: 'save_advantage',
19
+ condition: 'save_against_frightened',
20
+ effect: 'advantage',
21
+ description: 'Advantage on saves against being frightened'
22
+ },
23
+ 'halfling nimbleness': {
24
+ type: 'movement_enhancement',
25
+ effect: 'can_move_through_space_of_creature_larger_than_you',
26
+ description: 'Move through space of creatures larger than you'
27
+ },
28
+
29
+ // ===== DWARF FEATURES =====
30
+ 'dwarven resilience': {
31
+ type: 'damage_resistance_and_save_advantage',
32
+ damageType: 'poison',
33
+ saveAdvantage: 'poison_saves',
34
+ description: 'Poison resistance + advantage on poison saves'
35
+ },
36
+ 'stonecunning': {
37
+ type: 'skill_bonus',
38
+ condition: 'intelligence_check_recall_information_about_stonework',
39
+ effect: 'double_proficiency_bonus',
40
+ description: 'Double prof bonus on stonework history checks'
41
+ },
42
+ 'dwarven toughness': {
43
+ type: 'hp_increase',
44
+ effect: '+1_hp_per_level',
45
+ description: '+1 HP per level'
46
+ },
47
+
48
+ // ===== ELF FEATURES =====
49
+ 'fey ancestry': {
50
+ type: 'save_advantage_and_immunity',
51
+ saveAdvantage: 'charmed_saves',
52
+ immunity: 'magic_sleep',
53
+ description: 'Advantage on charm saves + immune to magic sleep'
54
+ },
55
+ 'trance': {
56
+ type: 'rest_replacement',
57
+ effect: 'meditate_4_hours_instead_of_sleep_8_hours',
58
+ description: 'Meditate 4 hours instead of sleeping 8 hours'
59
+ },
60
+ 'mask of the wild': {
61
+ type: 'stealth_enhancement',
62
+ condition: 'in_natural_surroundings',
63
+ effect: 'attempt_to_hide_even_only_lightly_obscured',
64
+ description: 'Hide in natural surroundings when only lightly obscured'
65
+ },
66
+
67
+ // ===== HUMAN FEATURES =====
68
+ 'variant human': {
69
+ type: 'customizable',
70
+ options: ['skill_proficiency', 'feat'],
71
+ description: 'Choose 1 skill proficiency + 1 feat'
72
+ },
73
+
74
+ // ===== DRAGONBORN FEATURES =====
75
+ 'breath weapon': {
76
+ type: 'area_damage',
77
+ actionType: 'action',
78
+ recharge: 'short_rest',
79
+ area: '15_foot_cone_30_foot_line',
80
+ damageFormula: '2d6',
81
+ damageType: 'chosen_draconic_ancestry',
82
+ saveType: 'dexterity',
83
+ saveDC: '8 + con_mod + prof_bonus',
84
+ description: 'Cone/line damage, Dex save for half'
85
+ },
86
+ 'dragonborn damage resistance': {
87
+ type: 'damage_resistance',
88
+ damageType: 'chosen_draconic_ancestry',
89
+ description: 'Resistance to chosen damage type'
90
+ },
91
+
92
+ // ===== GNOME FEATURES =====
93
+ 'gnome cunning': {
94
+ type: 'save_advantage',
95
+ saveTypes: ['intelligence', 'wisdom', 'charisma'],
96
+ effect: 'advantage',
97
+ description: 'Advantage on Int/Wis/Cha saves'
98
+ },
99
+ 'artificer\'s lore': {
100
+ type: 'skill_bonus',
101
+ condition: 'intelligence_check_magical_technological_item',
102
+ effect: 'double_proficiency_bonus',
103
+ description: 'Double prof bonus on magic/tech item checks'
104
+ },
105
+ 'tinker': {
106
+ type: 'crafting_ability',
107
+ options: ['tiny_clockwork_device', 'explosive_device', 'minor_magic_item'],
108
+ description: 'Create tiny clockwork devices, explosives, or minor magic items'
109
+ },
110
+
111
+ // ===== HALF-ORC FEATURES =====
112
+ 'relentless endurance': {
113
+ type: 'death_prevention',
114
+ trigger: 'reduced_to_0_hp_but_not_killed',
115
+ effect: 'drop_to_1_hp_instead',
116
+ resource: 'once_per_long_rest',
117
+ description: 'Drop to 1 HP instead of 0 (once per long rest)'
118
+ },
119
+ 'savage attacks': {
120
+ type: 'damage_bonus',
121
+ trigger: 'critical_hit_with_melee_weapon',
122
+ effect: 'add_one_damage_die',
123
+ description: 'Add one damage die to melee crits'
124
+ },
125
+
126
+ // ===== HALF-ELF FEATURES =====
127
+ 'skill versatility': {
128
+ type: 'skill_proficiency',
129
+ effect: 'two_skill_proficiencies',
130
+ description: 'Choose two skill proficiencies'
131
+ },
132
+ 'half-elf fey ancestry': {
133
+ type: 'save_advantage_and_immunity',
134
+ saveAdvantage: 'charmed_saves',
135
+ immunity: 'magic_sleep',
136
+ description: 'Advantage on charm saves + immune to magic sleep'
137
+ },
138
+
139
+ // ===== TIEFLING FEATURES =====
140
+ 'hellish resistance': {
141
+ type: 'damage_resistance',
142
+ damageType: 'fire',
143
+ description: 'Fire resistance'
144
+ },
145
+ 'innate spellcasting': {
146
+ type: 'innate_magic',
147
+ spells: ['thaumaturgy', 'hellish_rebuke', 'darkness'],
148
+ spellLevels: [0, 1, 2],
149
+ description: 'Innate ability to cast Thaumaturgy, Hellish Rebuke, Darkness'
150
+ },
151
+
152
+ // ===== AASIMAR FEATURES =====
153
+ 'celestial resistance': {
154
+ type: 'damage_resistance_and_save_advantage',
155
+ damageTypes: ['necrotic', 'radiant'],
156
+ saveAdvantage: 'charmed_frightened_saves',
157
+ description: 'Necrotic/radiant resistance + advantage on charm/frighten saves'
158
+ },
159
+ 'healing hands': {
160
+ type: 'healing',
161
+ resource: 'once_per_long_rest',
162
+ healingFormula: 'character_level',
163
+ action: 'touch',
164
+ description: 'Heal HP equal to your level (once per long rest)'
165
+ },
166
+ 'light bearer': {
167
+ type: 'light_cantrip',
168
+ spell: 'light',
169
+ description: 'Can cast Light cantrip at will'
170
+ },
171
+ 'necrotic shroud': {
172
+ type: 'area_effect',
173
+ actionType: 'bonus_action',
174
+ resource: 'once_per_long_rest',
175
+ area: '10_foot_radius',
176
+ duration: '1_minute',
177
+ effects: [
178
+ 'frightened_creatures_in_area',
179
+ 'extra_necrotic_damage_against_frightened'
180
+ ],
181
+ description: 'Frighten creatures in 10ft, extra necrotic damage vs frightened'
182
+ },
183
+ 'radiant consumption': {
184
+ type: 'area_damage_and_buff',
185
+ actionType: 'bonus_action',
186
+ resource: 'once_per_long_rest',
187
+ area: '10_foot_radius',
188
+ duration: '1_minute',
189
+ effects: [
190
+ 'radiant_damage_to_hostile_creatures',
191
+ 'radiant_damage_to_self',
192
+ 'bright_light'
193
+ ],
194
+ description: 'Radiant damage to enemies + self, bright light'
195
+ },
196
+ 'radiant soul': {
197
+ type: 'flight_and_damage',
198
+ actionType: 'bonus_action',
199
+ resource: 'once_per_long_rest',
200
+ duration: '1_minute',
201
+ effects: [
202
+ 'fly_speed_equal_to_walking_speed',
203
+ 'extra_radiant_damage'
204
+ ],
205
+ description: 'Fly + extra radiant damage'
206
+ },
207
+ 'transformed': {
208
+ type: 'transformation',
209
+ actionType: 'action',
210
+ resource: 'once_per_long_rest',
211
+ duration: '1_minute',
212
+ effects: [
213
+ 'special_armor_class',
214
+ 'special_weapons',
215
+ 'fear_aura',
216
+ 'once_per_turn_blinding_light'
217
+ ],
218
+ description: 'Transform with special AC, weapons, fear aura, blinding light'
219
+ },
220
+
221
+ // ===== FIRBOLG FEATURES =====
222
+ 'firbolg magic': {
223
+ type: 'innate_magic',
224
+ spells: ['detect_magic', 'disguise_self'],
225
+ description: 'Innate Detect Magic + Disguise Self'
226
+ },
227
+ 'hidden step': {
228
+ type: 'stealth',
229
+ actionType: 'bonus_action',
230
+ resource: 'once_per_short_rest',
231
+ effect: 'invisible_until_next_turn_or_attack_or_cast_spell',
232
+ description: 'Bonus action invisibility until next turn/attack/cast'
233
+ },
234
+ 'speech of beast and leaf': {
235
+ type: 'communication',
236
+ effects: [
237
+ 'communicate_with_beasts_and_plants',
238
+ 'cannot_be_charmed_or_frightened_by_elementals_or_fey'
239
+ ],
240
+ description: 'Talk to beasts/plants + immune to elemental/fey charm/frighten'
241
+ },
242
+ 'firbolg powerful build': {
243
+ type: 'carry_capacity',
244
+ effect: 'double_carry_capacity_push_pull_lift',
245
+ description: 'Double carry/push/pull/lift capacity'
246
+ },
247
+
248
+ // ===== GOLIATH FEATURES =====
249
+ 'stone\'s endurance': {
250
+ type: 'damage_reduction',
251
+ timing: 'reaction',
252
+ trigger: 'take_damage',
253
+ effect: 'reduce_damage_by_1d12_plus_con_mod',
254
+ resource: 'once_per_short_rest',
255
+ description: 'Reaction: reduce damage by 1d12 + Con mod'
256
+ },
257
+ 'goliath powerful build': {
258
+ type: 'carry_capacity',
259
+ effect: 'double_carry_capacity_push_pull_lift',
260
+ description: 'Double carry/push/pull/lift capacity'
261
+ },
262
+ 'mountain born': {
263
+ type: 'environmental_adaptation',
264
+ effects: ['cold_resistance', 'acclimated_to_high_altitude'],
265
+ description: 'Cold resistance + acclimated to high altitude'
266
+ },
267
+
268
+ // ===== KENKU FEATURES =====
269
+ 'mimicry': {
270
+ type: 'sound_imitation',
271
+ effect: 'mimic_sounds_heard',
272
+ limitation: 'cannot_create_new_sounds',
273
+ description: 'Mimic sounds heard, cannot create new sounds'
274
+ },
275
+ 'expert forgery': {
276
+ type: 'skill_bonus',
277
+ condition: 'forgery_duplications',
278
+ effect: 'add_double_proficiency_bonus',
279
+ description: 'Double prof bonus on forgery attempts'
280
+ },
281
+
282
+ // ===== LIZARDFOLK FEATURES =====
283
+ 'bite': {
284
+ type: 'natural_weapon',
285
+ damageFormula: '1d4',
286
+ damageType: 'piercing',
287
+ description: 'Natural bite attack'
288
+ },
289
+ 'cunning artisan': {
290
+ type: 'crafting_ability',
291
+ effect: 'craft_simple_weapon_from_corpse_bones',
292
+ limitation: 'one_per_long_rest',
293
+ description: 'Craft simple weapon from corpse/bone (1 per long rest)'
294
+ },
295
+ 'hold breath': {
296
+ type: 'survival_ability',
297
+ effect: 'hold_breath_for_15_minutes',
298
+ description: 'Hold breath for 15 minutes'
299
+ },
300
+ 'natural armor': {
301
+ type: 'armor_class',
302
+ baseAC: '13 + dexterity_modifier',
303
+ limitation: 'cannot_wear_armor',
304
+ description: 'AC 13 + Dex (no armor)'
305
+ },
306
+ 'hungry jaws': {
307
+ type: 'bonus_action_attack',
308
+ actionType: 'bonus_action',
309
+ resource: 'once_per_short_rest',
310
+ damageFormula: '1d6',
311
+ damageType: 'piercing',
312
+ condition: 'must_hit_with_attack',
313
+ description: 'Bonus action bite attack for 1d6 piercing'
314
+ },
315
+
316
+ // ===== TABAXI FEATURES =====
317
+ 'cat\'s claws': {
318
+ type: 'natural_weapon',
319
+ damageFormula: '1d4',
320
+ damageType: 'slashing',
321
+ description: 'Natural claw attacks (unarmed)'
322
+ },
323
+ 'cat\'s talent': {
324
+ type: 'skill_bonus',
325
+ skills: ['stealth', 'perception'],
326
+ effect: 'double_proficiency_bonus',
327
+ description: 'Double prof bonus in Stealth and Perception'
328
+ },
329
+ 'feline agility': {
330
+ type: 'movement_boost',
331
+ trigger: 'move_on_turn',
332
+ effect: 'double_speed_until_end_of_turn',
333
+ limitation: 'once_per_turn',
334
+ resetType: 'special', // Resets when you move 0 feet on a turn, NOT on rest
335
+ description: 'Double speed until end of turn. Recharges when you move 0 feet on a turn (not on rest).'
336
+ },
337
+
338
+ // ===== TRITON FEATURES =====
339
+ 'amphibious': {
340
+ type: 'environmental_adaptation',
341
+ effects: ['breathe_air_and_water', 'swim_speed_40ft'],
342
+ description: 'Breathe air/water + 40ft swim speed'
343
+ },
344
+ 'control air and water': {
345
+ type: 'elemental_control',
346
+ spells: ['fog_cloud', 'gust_of_wind', 'wall_of_water'],
347
+ description: 'Cast Fog Cloud, Gust of Wind, Wall of Water'
348
+ },
349
+ 'guardian of the depths': {
350
+ type: 'environmental_resistance',
351
+ condition: '10_minutes_in_crushing_pressure',
352
+ effect: 'resistance_to_cold_damage',
353
+ description: 'Cold resistance after 10min in crushing pressure'
354
+ },
355
+ 'emissary of the sea': {
356
+ type: 'communication',
357
+ effects: [
358
+ 'communicate_simple_ideas_with_beasts_aquatic',
359
+ 'understand_any_language_aquatic'
360
+ ],
361
+ description: 'Talk to aquatic beasts + understand aquatic languages'
362
+ },
363
+
364
+ // ===== VERDAN FEATURES =====
365
+ 'black blood healing': {
366
+ type: 'healing',
367
+ trigger: 'take_poison_damage',
368
+ effect: 'regain_hp_equal_to_poison_damage_taken',
369
+ description: 'Regain HP when taking poison damage'
370
+ },
371
+ 'persuasive': {
372
+ type: 'skill_bonus',
373
+ skills: ['persuasion', 'deception'],
374
+ effect: 'advantage',
375
+ description: 'Advantage on Persuasion and Deception checks'
376
+ },
377
+ 'limited telepathy': {
378
+ type: 'telepathy',
379
+ range: '30_feet',
380
+ condition: 'creatures_understand_at_least_one_language',
381
+ description: '30ft telepathy with creatures that know a language'
382
+ },
383
+
384
+ // ===== CHANGELING FEATURES =====
385
+ 'shapechanger': {
386
+ type: 'transformation',
387
+ actionType: 'action',
388
+ effect: 'polymorph_into_humanoid',
389
+ limitation: 'same_size_and_sex',
390
+ description: 'Shapechange into humanoid of same size/sex'
391
+ },
392
+ 'deceptive': {
393
+ type: 'skill_bonus',
394
+ skills: ['deception', 'stealth'],
395
+ effect: 'advantage',
396
+ description: 'Advantage on Deception and Stealth checks'
397
+ },
398
+
399
+ // ===== SATYR FEATURES =====
400
+ 'fey magic': {
401
+ type: 'innate_magic',
402
+ spells: ['druidcraft', 'charm_person'],
403
+ description: 'Innate Druidcraft + Charm Person'
404
+ },
405
+ 'mirthful leapers': {
406
+ type: 'movement_enhancement',
407
+ effects: [
408
+ 'jump_distance_doubled',
409
+ 'advantage_on_strength_athletics_checks_to_jump'
410
+ ],
411
+ description: 'Double jump distance + advantage on jump Athletics checks'
412
+ },
413
+ 'reveler': {
414
+ type: 'skill_bonus',
415
+ skills: ['acrobatics', 'persuasion'],
416
+ effect: 'advantage',
417
+ description: 'Advantage on Acrobatics and Persuasion checks'
418
+ },
419
+
420
+ // ===== OWLIN FEATURES =====
421
+ 'flight': {
422
+ type: 'flight',
423
+ speed: 'walking_speed',
424
+ limitation: 'medium_armor_only',
425
+ description: 'Fly at walking speed (medium armor only)'
426
+ },
427
+ 'silent hunt': {
428
+ type: 'stealth_enhancement',
429
+ effect: 'no_disadvantage_on_stealth_checks_from_perception',
430
+ description: 'No disadvantage on Stealth from Perception'
431
+ },
432
+
433
+ // ===== LEONIN FEATURES =====
434
+ 'daunting roar': {
435
+ type: 'area_effect',
436
+ actionType: 'action',
437
+ resource: 'once_per_short_rest',
438
+ area: '10_foot_radius',
439
+ duration: '1_minute',
440
+ effects: [
441
+ 'frightened_creatures_in_area',
442
+ 'creatures_can_use_save_to_end_effect_early'
443
+ ],
444
+ saveType: 'wisdom',
445
+ saveDC: '8 + strength_mod + prof_bonus',
446
+ description: 'Frighten creatures in 10ft for 1 minute (Wis save)'
447
+ },
448
+ 'leonin damage resistance': {
449
+ type: 'damage_resistance',
450
+ damageType: 'necrotic',
451
+ description: 'Necrotic resistance'
452
+ },
453
+ 'leonine agility': {
454
+ type: 'defense_bonus',
455
+ condition: 'not_wearing_heavy_armor',
456
+ effect: 'advantage_on_dexterity_saving_throws',
457
+ description: 'Advantage on Dex saves (not heavy armor)'
458
+ },
459
+
460
+ // ===== RAVENITE FEATURES =====
461
+ 'fire resistance': {
462
+ type: 'damage_resistance',
463
+ damageType: 'fire',
464
+ description: 'Fire resistance'
465
+ },
466
+ 'wings of the raven': {
467
+ type: 'flight',
468
+ speed: '30_feet',
469
+ limitation: 'no_heavy_armor',
470
+ description: '30ft fly speed (no heavy armor)'
471
+ },
472
+
473
+ // ===== GITH FEATURES =====
474
+ 'astral knowledge': {
475
+ type: 'skill_bonus',
476
+ effect: 'proficient_in_two_skills',
477
+ description: 'Choose two skill proficiencies'
478
+ },
479
+ 'gith psionics': {
480
+ type: 'innate_magic',
481
+ spells: ['mage_hand', 'jump', 'misty_step'],
482
+ description: 'Innate Mage Hand, Jump, Misty Step'
483
+ },
484
+ 'decadent mastery': {
485
+ type: 'skill_bonus',
486
+ effect: 'proficient_with_light_armor',
487
+ description: 'Light armor proficiency'
488
+ },
489
+ 'void resistance': {
490
+ type: 'damage_resistance_and_save_advantage',
491
+ damageTypes: ['psychic', 'force'],
492
+ saveAdvantage: 'charmed_frightened_saves',
493
+ description: 'Psychic/force resistance + advantage on charm/frighten saves'
494
+ },
495
+
496
+ // ===== MISSING RACES =====
497
+ 'warf forged': {
498
+ type: 'defense_calculation',
499
+ effect: 'integrated_protection',
500
+ description: 'AC calculation includes integrated protection'
501
+ },
502
+ 'warf constructed resilience': {
503
+ type: 'immunity',
504
+ effects: ['poison_resistance', 'disease_resistance', 'advantage_vs_poison_disease_saves'],
505
+ description: 'Various immunities and resistances'
506
+ },
507
+ 'aarakocra': {
508
+ type: 'flight_ability',
509
+ effect: 'fly_speed',
510
+ description: 'Flight speed'
511
+ },
512
+ 'bugbear surprise attack': {
513
+ type: 'surprise_attack',
514
+ effect: 'advantage_on_attack_against_surprised_creatures',
515
+ description: 'Surprise attack advantage'
516
+ },
517
+ 'bugbear long-limbed': {
518
+ type: 'reach_extension',
519
+ effect: '5ft_reach',
520
+ description: 'Long-limbed reach'
521
+ },
522
+ 'goblin fury of the small': {
523
+ type: 'damage_bonus',
524
+ condition: 'creature_larger_than_you',
525
+ effect: 'bonus_damage',
526
+ description: 'Bonus damage against larger creatures'
527
+ },
528
+ 'goblin nimble escape': {
529
+ type: 'disengage',
530
+ actionType: 'bonus_action',
531
+ effect: 'hide_as_bonus_action',
532
+ description: 'Hide as bonus action'
533
+ },
534
+ 'hobgoblin saving face': {
535
+ type: 'reroll',
536
+ trigger: 'failed_attack_or_check',
537
+ effect: 'reroll_with_advantage',
538
+ description: 'Reroll failed attack or check with advantage'
539
+ },
540
+ 'kobold pack tactics': {
541
+ type: 'attack_bonus',
542
+ condition: 'ally_within_5ft',
543
+ effect: 'advantage_on_attack',
544
+ description: 'Advantage on attack when ally within 5ft'
545
+ },
546
+ 'kobold sunlight sensitivity': {
547
+ type: 'disadvantage',
548
+ condition: 'in_sunlight',
549
+ effect: 'disadvantage_on_attacks_and_perception_checks',
550
+ description: 'Disadvantage in sunlight'
551
+ },
552
+ 'orc aggressive': {
553
+ type: 'movement_bonus',
554
+ actionType: 'bonus_action',
555
+ effect: 'dash_toward_enemy',
556
+ description: 'Bonus action dash toward enemy'
557
+ },
558
+ 'orc powerful build': {
559
+ type: 'carry_capacity',
560
+ effect: 'double_carrying_capacity',
561
+ description: 'Double carrying capacity'
562
+ },
563
+ 'yuan-ti pureblood': {
564
+ type: 'immunity',
565
+ effects: ['magic_resistance', 'poison_immunity'],
566
+ description: 'Magic resistance + poison immunity'
567
+ },
568
+ 'tortle natural armor': {
569
+ type: 'defense_bonus',
570
+ effect: 'ac_17_natural_armor',
571
+ description: 'Natural armor AC 17'
572
+ },
573
+ 'tortle shell defense': {
574
+ type: 'defense_action',
575
+ actionType: 'action',
576
+ effect: 'add_shield_bonus_to_ac',
577
+ description: 'Action to add shield bonus to AC'
578
+ },
579
+ 'grung poison skin': {
580
+ type: 'contact_poison',
581
+ effect: 'poison_on_contact',
582
+ description: 'Poison skin'
583
+ },
584
+ 'grung standing leap': {
585
+ type: 'movement_enhancement',
586
+ effect: 'standing_jump',
587
+ jumpDistance: 'height',
588
+ description: 'Standing jump equal to height'
589
+ },
590
+ 'centaur equine build': {
591
+ type: 'movement_type',
592
+ effects: ['cannot_be_ridden', 'no_climbing_swimming_costs_extra'],
593
+ description: 'Equine build limitations'
594
+ },
595
+ 'centaur hooves': {
596
+ type: 'attack',
597
+ actionType: 'action',
598
+ damageFormula: '2d4 + strength_mod',
599
+ description: 'Hooves attack'
600
+ },
601
+ 'centaur charge': {
602
+ type: 'attack_bonus',
603
+ condition: 'move_at_least_20ft_straight_line',
604
+ effect: 'bonus_damage',
605
+ description: 'Bonus damage on charge'
606
+ },
607
+
608
+ // ===== 2024 RACIAL CHANGES =====
609
+ 'lucky (2024)': {
610
+ type: 'advantage',
611
+ trigger: 'roll_1_on_attack_save_or_check',
612
+ condition: 'self_or_ally_within_30_feet',
613
+ effect: 'advantage_instead_of_reroll',
614
+ ruleset: '2024',
615
+ description: 'Now gives advantage instead of rerolls'
616
+ },
617
+ 'breath weapon (2024)': {
618
+ type: 'damage_aoe',
619
+ actionType: 'action',
620
+ damageFormula: '2d6_dragon_breath',
621
+ saveType: 'dexterity',
622
+ saveDC: '8 + proficiency + con_mod',
623
+ recharge: 'short_rest',
624
+ ruleset: '2024',
625
+ description: '2024 version of dragonborn breath weapon'
626
+ }
627
+ };
628
+
629
+ /**
630
+ * Check if a racial feature is an edge case
631
+ */
632
+ function isRacialFeatureEdgeCase(featureName) {
633
+ if (!featureName) return false;
634
+ const lowerName = featureName.toLowerCase().trim();
635
+ return RACIAL_FEATURE_EDGE_CASES.hasOwnProperty(lowerName);
636
+ }
637
+
638
+ /**
639
+ * Get racial feature edge case configuration
640
+ */
641
+ function getRacialFeatureEdgeCase(featureName) {
642
+ if (!featureName) return null;
643
+ const lowerName = featureName.toLowerCase().trim();
644
+ return RACIAL_FEATURE_EDGE_CASES[lowerName] || null;
645
+ }
646
+
647
+ /**
648
+ * Apply racial feature edge case modifications to action options
649
+ */
650
+ function applyRacialFeatureEdgeCaseModifications(feature, options) {
651
+ const edgeCase = getRacialFeatureEdgeCase(feature.name);
652
+ if (!edgeCase) {
653
+ return { options, skipNormalButtons: false };
654
+ }
655
+
656
+ const modifiedOptions = [...options];
657
+ let skipNormalButtons = false;
658
+
659
+ switch (edgeCase.type) {
660
+ case 'innate_magic':
661
+ // Add spell casting info
662
+ modifiedOptions.forEach(opt => {
663
+ opt.edgeCaseNote = `🔮 ${edgeCase.spells.join(', ')}`;
664
+ });
665
+ break;
666
+
667
+ case 'damage_resistance':
668
+ // Add resistance info
669
+ if (Array.isArray(edgeCase.damageTypes)) {
670
+ modifiedOptions.forEach(opt => {
671
+ opt.edgeCaseNote = `đŸ›Ąī¸ Resistance to: ${edgeCase.damageTypes.join(', ')}`;
672
+ });
673
+ } else {
674
+ modifiedOptions.forEach(opt => {
675
+ opt.edgeCaseNote = `đŸ›Ąī¸ Resistance to: ${edgeCase.damageType}`;
676
+ });
677
+ }
678
+ break;
679
+
680
+ case 'save_advantage':
681
+ // Add save advantage info
682
+ if (Array.isArray(edgeCase.saveTypes)) {
683
+ modifiedOptions.forEach(opt => {
684
+ opt.edgeCaseNote = `✅ Advantage on: ${edgeCase.saveTypes.join(', ')} saves`;
685
+ });
686
+ } else {
687
+ modifiedOptions.forEach(opt => {
688
+ opt.edgeCaseNote = `✅ Advantage on: ${edgeCase.saveAdvantage}`;
689
+ });
690
+ }
691
+ break;
692
+
693
+ case 'flight':
694
+ // Add flight info
695
+ modifiedOptions.forEach(opt => {
696
+ opt.edgeCaseNote = `đŸĒļ Fly ${edgeCase.speed} ${edgeCase.limitation ? `(${edgeCase.limitation})` : ''}`;
697
+ });
698
+ break;
699
+
700
+ case 'natural_weapon':
701
+ // Add natural weapon info
702
+ modifiedOptions.forEach(opt => {
703
+ opt.edgeCaseNote = `âš”ī¸ ${edgeCase.damageFormula} ${edgeCase.damageType}`;
704
+ });
705
+ break;
706
+
707
+ case 'telepathy':
708
+ // Add telepathy info
709
+ modifiedOptions.forEach(opt => {
710
+ opt.edgeCaseNote = `🧠 ${edgeCase.range} telepathy`;
711
+ });
712
+ break;
713
+
714
+ case 'skill_bonus':
715
+ // Add skill bonus info
716
+ if (Array.isArray(edgeCase.skills)) {
717
+ modifiedOptions.forEach(opt => {
718
+ opt.edgeCaseNote = `📚 Bonus to: ${edgeCase.skills.join(', ')}`;
719
+ });
720
+ } else {
721
+ modifiedOptions.forEach(opt => {
722
+ opt.edgeCaseNote = `📚 ${edgeCase.condition}`;
723
+ });
724
+ }
725
+ break;
726
+
727
+ default:
728
+ // Add description note for other types
729
+ if (edgeCase.description) {
730
+ modifiedOptions.forEach(opt => {
731
+ opt.edgeCaseNote = edgeCase.description;
732
+ });
733
+ }
734
+ break;
735
+ }
736
+
737
+ return { options: modifiedOptions, skipNormalButtons };
738
+ }
739
+
740
+ // Expose to globalThis for importScripts usage
741
+ if (typeof globalThis !== 'undefined') {
742
+ globalThis.RACIAL_FEATURE_EDGE_CASES = RACIAL_FEATURE_EDGE_CASES;
743
+ globalThis.isRacialFeatureEdgeCase = isRacialFeatureEdgeCase;
744
+ globalThis.getRacialFeatureEdgeCase = getRacialFeatureEdgeCase;
745
+ globalThis.applyRacialFeatureEdgeCaseModifications = applyRacialFeatureEdgeCaseModifications;
746
+ }