@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,660 @@
1
+ /**
2
+ * Combat Maneuvers Edge Cases Configuration
3
+ *
4
+ * This file contains all the combat maneuvers and special combat actions
5
+ * that need special handling beyond standard attack/damage/healing buttons.
6
+ */
7
+
8
+ const COMBAT_MANEUVER_EDGE_CASES = {
9
+ // ===== STANDARD COMBAT ACTIONS =====
10
+ 'grapple': {
11
+ type: 'contest_check',
12
+ actionType: 'action',
13
+ attackType: 'athletics',
14
+ defenseType: 'athletics_or_acrobatics',
15
+ effect: 'grappled_condition',
16
+ description: 'Athletics vs Athletics/Acrobatics to grapple target'
17
+ },
18
+ 'shove': {
19
+ type: 'contest_check',
20
+ actionType: 'action',
21
+ attackType: 'athletics',
22
+ defenseType: 'athletics_or_acrobatics',
23
+ effects: ['knock_prone', 'push_5_feet'],
24
+ description: 'Athletics vs Athletics/Acrobatics to shove prone or push 5ft'
25
+ },
26
+ 'opportunity attack': {
27
+ type: 'reaction',
28
+ timing: 'when_creature_leaves_your_reach',
29
+ action: 'weapon_attack',
30
+ limitation: 'one_per_creature_per_turn',
31
+ description: 'Reaction attack when creature leaves your reach'
32
+ },
33
+ 'ready action': {
34
+ type: 'conditional_action',
35
+ actionType: 'action',
36
+ trigger: 'user_specified_condition',
37
+ timing: 'reaction',
38
+ description: 'Set trigger condition, then react with specified action'
39
+ },
40
+ 'disarm': {
41
+ type: 'contest_check',
42
+ actionType: 'action',
43
+ attackType: 'attack_roll',
44
+ defenseType: 'athletics_or_acrobatics',
45
+ effect: 'target_drops_item',
46
+ description: 'Attack roll vs Athletics/Acrobatics to disarm target'
47
+ },
48
+ 'overrun': {
49
+ type: 'contest_check',
50
+ actionType: 'action',
51
+ attackType: 'athletics',
52
+ defenseType: 'athletics_or_acrobatics',
53
+ effect: 'move_through_space',
54
+ description: 'Athletics vs Athletics/Acrobatics to move through space'
55
+ },
56
+
57
+ // ===== BATTLE MASTER MANEUVERS =====
58
+ 'bait and switch': {
59
+ type: 'defensive_swap',
60
+ cost: '1_superiority_die',
61
+ timing: 'bonus_action',
62
+ condition: 'move_within_5ft_of_ally',
63
+ effect: 'swap_ac_bonuses',
64
+ duration: '1_minute',
65
+ description: 'Move near ally to swap AC bonuses for 1 minute'
66
+ },
67
+ 'brace': {
68
+ type: 'reaction_attack',
69
+ cost: '1_superiority_die',
70
+ timing: 'reaction',
71
+ condition: 'creature_moves_into_your_reach',
72
+ action: 'weapon_attack',
73
+ description: 'Reaction attack when creature moves into reach'
74
+ },
75
+ 'commander\'s strike': {
76
+ type: 'ally_reaction',
77
+ cost: '1_superiority_die',
78
+ timing: 'on_your_turn',
79
+ condition: 'forgo_one_attack',
80
+ effect: 'ally_reaction_attack',
81
+ description: 'Forgo attack to give ally reaction attack'
82
+ },
83
+ 'disarming attack': {
84
+ type: 'attack_with_debuff',
85
+ cost: '1_superiority_die',
86
+ timing: 'on_hit',
87
+ condition: 'hit_attack',
88
+ saveType: 'strength',
89
+ saveFailure: 'drops_one_item',
90
+ description: 'Hit + Str save or target drops item'
91
+ },
92
+ 'distracting strike': {
93
+ type: 'attack_with_debuff',
94
+ cost: '1_superiority_die',
95
+ timing: 'on_hit',
96
+ condition: 'hit_attack',
97
+ effect: 'next_attack_disadvantage',
98
+ duration: 'until_your_next_turn',
99
+ description: 'Hit - next attack vs target has disadvantage'
100
+ },
101
+ 'evasive footwork': {
102
+ type: 'bonus_action_defense',
103
+ cost: '1_superiority_die',
104
+ timing: 'bonus_action',
105
+ condition: 'when_you_move',
106
+ effect: 'add_superiority_die_to_ac',
107
+ duration: 'until_you_stop_moving',
108
+ description: 'Move + add superiority die to AC while moving'
109
+ },
110
+ 'feinting attack': {
111
+ type: 'bonus_action_setup',
112
+ cost: '1_superiority_die',
113
+ timing: 'bonus_action',
114
+ condition: 'before_attack',
115
+ effect: 'advantage_on_next_attack',
116
+ description: 'Bonus action feint for advantage on next attack'
117
+ },
118
+ 'goading attack': {
119
+ type: 'attack_with_debuff',
120
+ cost: '1_superiority_die',
121
+ timing: 'on_hit',
122
+ condition: 'hit_attack',
123
+ saveType: 'wisdom',
124
+ saveFailure: 'disadvantage_on_attacks_against_others',
125
+ duration: '1_minute',
126
+ description: 'Hit - Wis save or target has disadvantage on attacks vs others'
127
+ },
128
+ 'lunging attack': {
129
+ type: 'attack_enhancement',
130
+ cost: '1_superiority_die',
131
+ timing: 'when_making_attack',
132
+ effect: 'increase_reach_by_5_feet',
133
+ description: 'Increase reach by 5ft for this attack'
134
+ },
135
+ 'maneuvering attack': {
136
+ type: 'ally_movement',
137
+ cost: '1_superiority_die',
138
+ timing: 'on_hit',
139
+ condition: 'hit_attack',
140
+ effect: 'ally_reaction_move',
141
+ distance: 'half_speed',
142
+ description: 'Hit - ally can reaction move half speed without provoking'
143
+ },
144
+ 'menacing attack': {
145
+ type: 'attack_with_debuff',
146
+ cost: '1_superiority_die',
147
+ timing: 'on_hit',
148
+ condition: 'hit_attack',
149
+ saveType: 'wisdom',
150
+ saveFailure: 'frightened_condition',
151
+ duration: '1_minute',
152
+ description: 'Hit - Wis save or target is frightened'
153
+ },
154
+ 'pushing attack': {
155
+ type: 'attack_with_debuff',
156
+ cost: '1_superiority_die',
157
+ timing: 'on_hit',
158
+ condition: 'hit_attack',
159
+ saveType: 'strength',
160
+ saveFailure: 'push_15_feet',
161
+ description: 'Hit - Str save or push target 15ft'
162
+ },
163
+ 'rally': {
164
+ type: 'ally_buff',
165
+ cost: '1_superiority_die',
166
+ timing: 'bonus_action',
167
+ condition: 'ally_can_see_or_hear_you',
168
+ effect: 'temp_hp',
169
+ formula: 'superiority_die',
170
+ description: 'Bonus action - ally gains temp HP equal to superiority die'
171
+ },
172
+ 'riposte': {
173
+ type: 'reaction_attack',
174
+ cost: '1_superiority_die',
175
+ timing: 'reaction',
176
+ condition: 'melee_attack_misses_you',
177
+ action: 'weapon_attack',
178
+ description: 'Reaction attack when melee attack misses you'
179
+ },
180
+ 'sweeping attack': {
181
+ type: 'area_damage',
182
+ cost: '1_superiority_die',
183
+ timing: 'on_hit',
184
+ condition: 'hit_creature_with_another_enemy_within_5ft',
185
+ effect: 'damage_to_second_creature',
186
+ formula: 'superiority_die',
187
+ description: 'Hit creature - second creature within 5ft takes superiority die damage'
188
+ },
189
+ 'trip attack': {
190
+ type: 'attack_with_debuff',
191
+ cost: '1_superiority_die',
192
+ timing: 'on_hit',
193
+ condition: 'hit_attack',
194
+ saveType: 'strength',
195
+ saveFailure: 'knocked_prone',
196
+ description: 'Hit - Str save or target is knocked prone'
197
+ },
198
+
199
+ // ===== OPTIONAL COMBAT RULES =====
200
+ 'cleave': {
201
+ type: 'bonus_attack',
202
+ condition: 'reduce_creature_to_0_hp_with_melee_attack',
203
+ effect: 'attack_another_creature_within_reach',
204
+ limitation: 'once_per_turn',
205
+ description: 'Reduce creature to 0 HP - bonus attack against another creature'
206
+ },
207
+ 'mark': {
208
+ type: 'debuff',
209
+ actionType: 'bonus_action',
210
+ condition: 'hit_creature_with_weapon_attack',
211
+ effect: 'disadvantage_on_attacks_against_others',
212
+ duration: 'until_your_next_turn',
213
+ description: 'Hit - target has disadvantage on attacks vs others'
214
+ },
215
+ 'shove aside': {
216
+ type: 'movement_control',
217
+ condition: 'hit_creature_with_melee_attack',
218
+ effect: 'push_creature_5_feet_away',
219
+ description: 'Hit - push creature 5ft away'
220
+ },
221
+
222
+ // ===== SPECIAL COMBAT SITUATIONS =====
223
+ 'flanking': {
224
+ type: 'situational_advantage',
225
+ condition: 'ally_opposite_side_of_enemy',
226
+ effect: 'advantage_on_melee_attacks',
227
+ description: 'Advantage on melee attacks when flanking with ally'
228
+ },
229
+ 'help': {
230
+ type: 'advantage_grant',
231
+ actionType: 'action',
232
+ effect: 'advantage_on_next_ability_check_or_attack_roll',
233
+ target: 'ally',
234
+ description: 'Give ally advantage on next check/attack'
235
+ },
236
+ 'dodge': {
237
+ type: 'defensive_action',
238
+ actionType: 'action',
239
+ effects: [
240
+ 'attacks_against_you_have_disadvantage',
241
+ 'dex_saves_advantage'
242
+ ],
243
+ duration: 'until_your_next_turn',
244
+ description: 'Disadvantage on attacks vs you + advantage on Dex saves'
245
+ },
246
+ 'hide': {
247
+ type: 'stealth_action',
248
+ actionType: 'action',
249
+ requirement: 'cannot_be_seen',
250
+ effect: 'hidden_condition',
251
+ description: 'Become hidden if not seen'
252
+ },
253
+ 'search': {
254
+ type: 'investigation_action',
255
+ actionType: 'action',
256
+ effect: 'make_intelligence_investigation_check',
257
+ description: 'Search area with Investigation check'
258
+ },
259
+ 'improvise weapon': {
260
+ type: 'weapon_substitution',
261
+ effect: 'use_any_object_as_weapon',
262
+ damage: '1d4',
263
+ damageType: 'varies_by_object',
264
+ description: 'Use any object as improvised weapon (1d4 damage)'
265
+ },
266
+
267
+ // ===== TWO-WEAPON FIGHTING =====
268
+ 'two-weapon fighting': {
269
+ type: 'bonus_action_attack',
270
+ condition: 'taking_attack_action_with_light_melee_weapon',
271
+ requirement: 'must_have_light_melee_weapon_in_other_hand',
272
+ effect: 'bonus_attack_with_offhand_weapon',
273
+ damage: 'no_ability_modifier_to_damage',
274
+ description: 'Bonus action attack with offhand light weapon (no mod to damage)'
275
+ },
276
+
277
+ // ===== DUAL WIELDER FEAT =====
278
+ 'dual wielder': {
279
+ type: 'two_weapon_enhancement',
280
+ effects: [
281
+ 'no_light_weapon_requirement',
282
+ 'use_two_handed_melee_weapon_in_one_hand',
283
+ '+1_ac_while_dual_wielding'
284
+ ],
285
+ description: 'Dual wield non-light weapons +1 AC'
286
+ },
287
+
288
+ // ===== GRAPPLING SPECIAL CASES =====
289
+ 'escape grapple': {
290
+ type: 'contest_check',
291
+ actionType: 'action',
292
+ attackType: 'athletics_or_acrobatics',
293
+ defenseType: 'athletics',
294
+ effect: 'end_grappled_condition',
295
+ description: 'Athletics/Acrobatics vs Athletics to escape grapple'
296
+ },
297
+ 'restrain with grapple': {
298
+ type: 'contest_check',
299
+ actionType: 'action',
300
+ condition: 'already_grappling_target',
301
+ attackType: 'athletics',
302
+ defenseType: 'athletics',
303
+ effect: 'restrained_condition',
304
+ description: 'Athletics vs Athletics to restrain grappled target'
305
+ },
306
+
307
+ // ===== SHIELD SPECIAL CASES =====
308
+ 'shield bash': {
309
+ type: 'bonus_action_attack',
310
+ condition: 'wielding_shield',
311
+ effect: 'melee_weapon_attack_with_shield',
312
+ damage: '1d4_bludgeoning',
313
+ description: 'Bonus action shield bash (1d4 bludgeoning)'
314
+ },
315
+ 'shield master feat': {
316
+ type: 'bonus_action_combo',
317
+ condition: 'take_attack_action_while_wielding_shield',
318
+ effect: 'shove_as_bonus_action',
319
+ description: 'Attack action + bonus action shove with shield'
320
+ },
321
+
322
+ // ===== MOUNTED COMBAT =====
323
+ 'mount combat': {
324
+ type: 'mounted_benefits',
325
+ effects: [
326
+ 'advantage_on_attacks_against_creatures_smaller_than_mount',
327
+ 'mount_act_as_separate_creature',
328
+ 'can_use_mount_as_cover'
329
+ ],
330
+ description: 'Various benefits while mounted'
331
+ },
332
+ 'dismount': {
333
+ type: 'movement_action',
334
+ actionType: 'half_action',
335
+ effect: 'dismount_from_mount',
336
+ description: 'Half action to dismount'
337
+ },
338
+
339
+ // ===== UNDERWATER COMBAT =====
340
+ 'underwater combat': {
341
+ type: 'environmental_modifier',
342
+ effects: [
343
+ 'ranged_weapon_attacks_have_disadvantage',
344
+ 'melee_weapon_attacks_with_thrown_weapons_have_disadvantage',
345
+ 'creatures_without_swim_speed_have_disadvantage_on_attacks'
346
+ ],
347
+ description: 'Underwater combat penalties'
348
+ },
349
+
350
+ // ===== COVER =====
351
+ 'half cover': {
352
+ type: 'defense_bonus',
353
+ effect: '+2_to_ac_and_dex_saves',
354
+ description: '+2 AC and Dex saves'
355
+ },
356
+ 'three-quarters cover': {
357
+ type: 'defense_bonus',
358
+ effect: '+5_to_ac_and_dex_saves',
359
+ description: '+5 AC and Dex saves'
360
+ },
361
+ 'total cover': {
362
+ type: 'defense_immunity',
363
+ effect: 'cannot_be_targeted_by_attacks_or_spells',
364
+ description: 'Cannot be targeted by attacks or spells'
365
+ },
366
+
367
+ // ===== PRONE CONDITION =====
368
+ 'stand up': {
369
+ type: 'movement_action',
370
+ actionType: 'half_action',
371
+ effect: 'end_prone_condition',
372
+ description: 'Half action to stand from prone'
373
+ },
374
+ 'attack prone creature': {
375
+ type: 'attack_modifier',
376
+ condition: 'target_prone_and_within_5_feet',
377
+ effect: 'advantage_on_melee_attacks',
378
+ description: 'Advantage on melee attacks vs prone within 5ft'
379
+ },
380
+ 'ranged attack prone creature': {
381
+ type: 'attack_modifier',
382
+ condition: 'target_prone_and_beyond_5_feet',
383
+ effect: 'disadvantage_on_ranged_attacks',
384
+ description: 'Disadvantage on ranged attacks vs prone beyond 5ft'
385
+ },
386
+
387
+ // ===== MISSING COMBAT MANEUVERS =====
388
+ 'climbing onto a bigger creature': {
389
+ type: 'contest_check',
390
+ actionType: 'action',
391
+ attackType: 'athletics',
392
+ defenseType: 'athletics_or_acrobatics',
393
+ condition: 'target_larger_than_you',
394
+ effect: 'grapple_and_mount',
395
+ description: 'Contest to grapple/mount larger creature'
396
+ },
397
+ 'tumble': {
398
+ type: 'contest_check',
399
+ actionType: 'bonus_action',
400
+ attackType: 'acrobatics',
401
+ defenseType: 'acrobatics',
402
+ effect: 'move_through_hostile_space',
403
+ description: 'Acrobatics vs Acrobatics to move through hostile space'
404
+ },
405
+ 'called shot': {
406
+ type: 'attack_modifier',
407
+ actionType: 'action',
408
+ effect: 'disadvantage_for_specific_effect',
409
+ condition: 'dm_discretion',
410
+ description: 'Disadvantage to attack for specific effect (DM discretion)'
411
+ },
412
+ 'disarm self': {
413
+ type: 'free_action',
414
+ actionType: 'free_action',
415
+ effect: 'drop_item',
416
+ description: 'Drop item as free action'
417
+ },
418
+ 'don shield': {
419
+ type: 'equipment_action',
420
+ actionType: 'action',
421
+ effect: 'equip_shield',
422
+ description: 'Action to equip shield'
423
+ },
424
+ 'doff shield': {
425
+ type: 'equipment_action',
426
+ actionType: 'action',
427
+ effect: 'remove_shield',
428
+ description: 'Action to remove shield'
429
+ },
430
+ 'don armor': {
431
+ type: 'equipment_action',
432
+ actionType: 'time_based_action',
433
+ effect: 'equip_armor',
434
+ description: 'Time-based action to equip armor'
435
+ },
436
+ 'doff armor': {
437
+ type: 'equipment_action',
438
+ actionType: 'time_based_action',
439
+ effect: 'remove_armor',
440
+ description: 'Time-based action to remove armor'
441
+ },
442
+
443
+ // ===== 2024 COMBAT MANEUVER CHANGES =====
444
+ 'grapple (2024)': {
445
+ type: 'contest_check',
446
+ actionType: 'action',
447
+ attackType: 'athletics',
448
+ defenseType: 'athletics_or_acrobatics',
449
+ effect: 'grappled_condition',
450
+ ruleset: '2024',
451
+ description: '2024 version of grapple rules'
452
+ },
453
+ 'shove (2024)': {
454
+ type: 'contest_check',
455
+ actionType: 'action',
456
+ attackType: 'athletics',
457
+ defenseType: 'athletics_or_acrobatics',
458
+ effects: ['knock_prone', 'push_5_feet'],
459
+ ruleset: '2024',
460
+ description: '2024 version of shove rules'
461
+ },
462
+
463
+ // ===== 2024 WEAPON MASTERIES (NEW SYSTEM) =====
464
+ 'cleave (weapon mastery)': {
465
+ type: 'weapon_mastery',
466
+ condition: 'hit_creature',
467
+ effect: 'hit_second_target_within_5ft',
468
+ description: 'Hit second target within 5ft'
469
+ },
470
+ 'graze': {
471
+ type: 'weapon_mastery',
472
+ condition: 'miss_attack',
473
+ effect: 'damage_equal_to_ability_mod',
474
+ description: 'Miss still deals damage equal to ability mod'
475
+ },
476
+ 'nick': {
477
+ type: 'weapon_mastery',
478
+ condition: 'light_weapon',
479
+ effect: 'extra_attack_no_bonus_action',
480
+ description: 'Make extra attack with light weapon (no bonus action needed)'
481
+ },
482
+ 'push': {
483
+ type: 'weapon_mastery',
484
+ condition: 'hit_creature',
485
+ effect: 'push_10ft',
486
+ description: 'Push 10ft on hit'
487
+ },
488
+ 'sap': {
489
+ type: 'weapon_mastery',
490
+ condition: 'hit_creature',
491
+ effect: 'disadvantage_on_next_attack',
492
+ description: 'Disadvantage on next attack'
493
+ },
494
+ 'slow': {
495
+ type: 'weapon_mastery',
496
+ condition: 'hit_creature',
497
+ effect: 'reduce_speed_by_10ft',
498
+ description: 'Reduce speed by 10ft'
499
+ },
500
+ 'topple': {
501
+ type: 'weapon_mastery',
502
+ condition: 'hit_creature',
503
+ effect: 'knock_prone_on_failed_con_save',
504
+ description: 'Knock prone on failed CON save'
505
+ },
506
+ 'vex': {
507
+ type: 'weapon_mastery',
508
+ condition: 'hit_creature',
509
+ effect: 'advantage_on_next_attack_vs_same_target',
510
+ description: 'Advantage on next attack vs same target'
511
+ },
512
+
513
+ // ===== 2024 FEATS SYSTEM =====
514
+ 'alert (2024)': {
515
+ type: 'initiative_bonus',
516
+ effect: 'add_proficiency_to_initiative',
517
+ additionalEffect: 'cannot_be_surprised',
518
+ ruleset: '2024',
519
+ description: '+Initiative equal to proficiency, cannot be surprised'
520
+ },
521
+ 'lucky (2024 feat)': {
522
+ type: 'advantage',
523
+ effect: 'advantage_instead_of_reroll',
524
+ ruleset: '2024',
525
+ description: 'Now gives advantage instead of rerolls'
526
+ },
527
+ 'great weapon master (2024)': {
528
+ type: 'attack_option',
529
+ choice: 'redesigned_system',
530
+ ruleset: '2024',
531
+ description: 'Redesigned completely'
532
+ },
533
+ 'sharpshooter (2024)': {
534
+ type: 'attack_option',
535
+ choice: 'redesigned_system',
536
+ ruleset: '2024',
537
+ description: 'Redesigned completely'
538
+ }
539
+ };
540
+
541
+ /**
542
+ * Check if a combat maneuver is an edge case
543
+ */
544
+ function isCombatManeuverEdgeCase(maneuverName) {
545
+ if (!maneuverName) return false;
546
+ const lowerName = maneuverName.toLowerCase().trim();
547
+ return COMBAT_MANEUVER_EDGE_CASES.hasOwnProperty(lowerName);
548
+ }
549
+
550
+ /**
551
+ * Get combat maneuver edge case configuration
552
+ */
553
+ function getCombatManeuverEdgeCase(maneuverName) {
554
+ if (!maneuverName) return null;
555
+ const lowerName = maneuverName.toLowerCase().trim();
556
+ return COMBAT_MANEUVER_EDGE_CASES[lowerName] || null;
557
+ }
558
+
559
+ /**
560
+ * Apply combat maneuver edge case modifications to action options
561
+ */
562
+ function applyCombatManeuverEdgeCaseModifications(maneuver, options) {
563
+ const edgeCase = getCombatManeuverEdgeCase(maneuver.name);
564
+ if (!edgeCase) {
565
+ return { options, skipNormalButtons: false };
566
+ }
567
+
568
+ const modifiedOptions = [...options];
569
+ let skipNormalButtons = false;
570
+
571
+ switch (edgeCase.type) {
572
+ case 'contest_check':
573
+ // Add contest check info
574
+ modifiedOptions.forEach(opt => {
575
+ opt.edgeCaseNote = `⚔️ ${edgeCase.attackType} vs ${edgeCase.defenseType}`;
576
+ });
577
+ break;
578
+
579
+ case 'reaction':
580
+ case 'reaction_attack':
581
+ // Add reaction timing info
582
+ modifiedOptions.forEach(opt => {
583
+ opt.edgeCaseNote = `⚡ ${edgeCase.timing}`;
584
+ });
585
+ break;
586
+
587
+ case 'bonus_action':
588
+ case 'bonus_action_attack':
589
+ case 'bonus_action_defense':
590
+ case 'bonus_action_setup':
591
+ // Add bonus action indicator
592
+ modifiedOptions.forEach(opt => {
593
+ opt.edgeCaseNote = `⚡ Bonus Action`;
594
+ });
595
+ break;
596
+
597
+ case 'attack_with_debuff':
598
+ // Add debuff info
599
+ modifiedOptions.forEach(opt => {
600
+ opt.edgeCaseNote = `🎯 Hit + ${edgeCase.saveType} save or ${edgeCase.saveFailure}`;
601
+ });
602
+ break;
603
+
604
+ case 'ally_reaction':
605
+ case 'ally_movement':
606
+ case 'ally_buff':
607
+ // Add ally interaction info
608
+ modifiedOptions.forEach(opt => {
609
+ opt.edgeCaseNote = `🤝 ${edgeCase.effect}`;
610
+ });
611
+ break;
612
+
613
+ case 'area_damage':
614
+ // Add area damage info
615
+ modifiedOptions.forEach(opt => {
616
+ opt.edgeCaseNote = `💥 ${edgeCase.condition}: ${edgeCase.effect}`;
617
+ });
618
+ break;
619
+
620
+ case 'situational_advantage':
621
+ // Add situational advantage info
622
+ modifiedOptions.forEach(opt => {
623
+ opt.edgeCaseNote = `✅ ${edgeCase.condition}`;
624
+ });
625
+ break;
626
+
627
+ case 'defensive_action':
628
+ // Add defensive action info
629
+ modifiedOptions.forEach(opt => {
630
+ opt.edgeCaseNote = `🛡️ ${edgeCase.effects.join(', ')}`;
631
+ });
632
+ break;
633
+
634
+ case 'environmental_modifier':
635
+ // Add environmental modifier info
636
+ modifiedOptions.forEach(opt => {
637
+ opt.edgeCaseNote = `🌍 ${edgeCase.effects.join(', ')}`;
638
+ });
639
+ break;
640
+
641
+ default:
642
+ // Add description note for other types
643
+ if (edgeCase.description) {
644
+ modifiedOptions.forEach(opt => {
645
+ opt.edgeCaseNote = edgeCase.description;
646
+ });
647
+ }
648
+ break;
649
+ }
650
+
651
+ return { options: modifiedOptions, skipNormalButtons };
652
+ }
653
+
654
+ // Expose to globalThis for importScripts usage
655
+ if (typeof globalThis !== 'undefined') {
656
+ globalThis.COMBAT_MANEUVER_EDGE_CASES = COMBAT_MANEUVER_EDGE_CASES;
657
+ globalThis.isCombatManeuverEdgeCase = isCombatManeuverEdgeCase;
658
+ globalThis.getCombatManeuverEdgeCase = getCombatManeuverEdgeCase;
659
+ globalThis.applyCombatManeuverEdgeCaseModifications = applyCombatManeuverEdgeCaseModifications;
660
+ }