diff --git a/assets/anice/manifest.js b/assets/anice/manifest.js
index 0637ec0..c85c3bf 100644
--- a/assets/anice/manifest.js
+++ b/assets/anice/manifest.js
@@ -1,2 +1,2 @@
window.FIGHTERS=window.FIGHTERS||{};
-window.FIGHTERS.anice={"id": "anice", "name": "A NICE", "tag": "THE NARRATOR", "race": "HUMAN", "title": "NARRATOR", "color": "#5aa9f0", "status": "ON THE PODIUM", "select": "assets/menu/anice-select.webp", "detail": "Polite, precise, and every sentence lands.", "style": "Zoner / words and podium", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["JAB NARASI", "GAGASAN BEAM", "MIMBAR SLAM", "PIDATO PANJANG"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "projectile", "damage": 14, "speed": 820, "range": 580, "size": 170, "knockback": 180, "duration": 0.6, "hitAt": 0.5, "asset": "words"}, "skill2": {"kind": "area", "damage": 20, "ahead": 110, "radius": 115, "vy": 140, "knockback": 260, "duration": 0.62, "hitAt": 0.55, "asset": "podium"}}, "ultimate": {"kind": "volley", "asset": "words", "count": 16, "gap": 0.09, "damage": 3, "speed": 950, "size": 110, "homing": true, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 26, "asset": "burst"}}, "fx": ["burst", "impact", "nano", "nanoblast", "podium", "tackle", "words"], "cutin": {"a": "#0a1830", "b": "#245a9e", "kicker": "#d8ecff", "title": "PIDATO
PANJANG", "detail": "A STORM OF WORDS"}, "manifest": {"characterId": "anice", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "green", "hex": "#00FF00", "rgb": [0, 255, 0], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -58, "right": 61, "top": -177, "bottom": 0}, "height": 177}, {"frame": 1, "bounds": {"left": -57, "right": 61, "top": -185, "bottom": 0}, "height": 185}, {"frame": 2, "bounds": {"left": -58, "right": 60, "top": -176, "bottom": 0}, "height": 176}, {"frame": 3, "bounds": {"left": -58, "right": 60, "top": -184, "bottom": 0}, "height": 184}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -57, "right": 61, "top": -203, "bottom": 0}, "height": 203}, {"frame": 1, "bounds": {"left": -33, "right": 55, "top": -202, "bottom": 0}, "height": 202}, {"frame": 2, "bounds": {"left": -65, "right": 67, "top": -203, "bottom": 0}, "height": 203}, {"frame": 3, "bounds": {"left": -56, "right": 49, "top": -202, "bottom": 0}, "height": 202}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -90, "right": 30, "top": -166, "bottom": 0}, "height": 166}, {"frame": 1, "bounds": {"left": -25, "right": 75, "top": -166, "bottom": 0}, "height": 166}, {"frame": 2, "bounds": {"left": -98, "right": 37, "top": -164, "bottom": 0}, "height": 164}, {"frame": 3, "bounds": {"left": -24, "right": 83, "top": -167, "bottom": 0}, "height": 167}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -20, "right": 77, "top": -154, "bottom": 0}, "height": 154}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -27, "right": 29, "top": -112, "bottom": 0}, "height": 112}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -56, "right": 65, "top": -156, "bottom": 0}, "height": 156}, {"frame": 1, "bounds": {"left": -50, "right": 59, "top": -137, "bottom": 0}, "height": 137}, {"frame": 2, "bounds": {"left": -57, "right": 62, "top": -150, "bottom": 0}, "height": 150}, {"frame": 3, "bounds": {"left": -56, "right": 61, "top": -163, "bottom": 0}, "height": 163}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -52, "right": 58, "top": -172, "bottom": 0}, "height": 172}, {"frame": 1, "bounds": {"left": -58, "right": 77, "top": -172, "bottom": 0}, "height": 172}, {"frame": 2, "bounds": {"left": -60, "right": 92, "top": -171, "bottom": 0}, "height": 171}, {"frame": 3, "bounds": {"left": -57, "right": 61, "top": -172, "bottom": 0}, "height": 172}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -69, "right": 66, "top": -186, "bottom": 0}, "height": 186}, {"frame": 1, "bounds": {"left": -65, "right": 68, "top": -189, "bottom": 0}, "height": 189}, {"frame": 2, "bounds": {"left": -67, "right": 96, "top": -191, "bottom": 0}, "height": 191}, {"frame": 3, "bounds": {"left": -66, "right": 67, "top": -193, "bottom": 0}, "height": 193}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 63, "top": -171, "bottom": 0}, "height": 171}, {"frame": 1, "bounds": {"left": -64, "right": 87, "top": -172, "bottom": 0}, "height": 172}, {"frame": 2, "bounds": {"left": -73, "right": 105, "top": -173, "bottom": 0}, "height": 173}, {"frame": 3, "bounds": {"left": -60, "right": 62, "top": -189, "bottom": 0}, "height": 189}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 61, "top": -193, "bottom": 0}, "height": 193}, {"frame": 1, "bounds": {"left": -59, "right": 73, "top": -194, "bottom": 0}, "height": 194}, {"frame": 2, "bounds": {"left": -60, "right": 98, "top": -187, "bottom": 0}, "height": 187}, {"frame": 3, "bounds": {"left": -56, "right": 63, "top": -193, "bottom": 0}, "height": 193}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 63, "top": -186, "bottom": 0}, "height": 186}, {"frame": 1, "bounds": {"left": -64, "right": 65, "top": -170, "bottom": 0}, "height": 170}, {"frame": 2, "bounds": {"left": -67, "right": 65, "top": -159, "bottom": 0}, "height": 159}, {"frame": 3, "bounds": {"left": -67, "right": 67, "top": -182, "bottom": 0}, "height": 182}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -46, "right": 49, "top": -184, "bottom": 0}, "height": 184}, {"frame": 1, "bounds": {"left": -75, "right": 78, "top": -184, "bottom": 0}, "height": 184}, {"frame": 2, "bounds": {"left": -60, "right": 80, "top": -181, "bottom": 0}, "height": 181}, {"frame": 3, "bounds": {"left": -60, "right": 81, "top": -182, "bottom": 0}, "height": 182}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 66, "top": -175, "bottom": 0}, "height": 175}, {"frame": 1, "bounds": {"left": -54, "right": 65, "top": -192, "bottom": 0}, "height": 192}, {"frame": 2, "bounds": {"left": -59, "right": 66, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -65, "right": 67, "top": -194, "bottom": 0}, "height": 194}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -58, "right": 46, "top": -137, "bottom": 0}, "height": 137}, {"frame": 1, "bounds": {"left": -104, "right": 22, "top": -124, "bottom": 0}, "height": 124}, {"frame": 2, "bounds": {"left": -74, "right": 69, "top": -60, "bottom": 0}, "height": 60}, {"frame": 3, "bounds": {"left": -73, "right": 77, "top": -37, "bottom": 0}, "height": 37}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -86, "right": 70, "top": -64, "bottom": 0}, "height": 64}, {"frame": 1, "bounds": {"left": -71, "right": 62, "top": -102, "bottom": 0}, "height": 102}, {"frame": 2, "bounds": {"left": -46, "right": 59, "top": -130, "bottom": 0}, "height": 130}, {"frame": 3, "bounds": {"left": -63, "right": 63, "top": -184, "bottom": 0}, "height": 184}]}}, "emitters": {"attack1": {"x": 92, "y": -127, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 96, "y": -142, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 105, "y": -127, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 98, "y": -152, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 65, "y": -6, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 80, "y": -145, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 28, "y": -77}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 1, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "SENATOR", "label": "SENATOR", "announce": "MODE SENATOR!", "duration": 10, "damageMul": 1.5, "speedMul": 1.1, "reachAdd": 30, "aura": "#ffd24a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["TINJU SENATOR", "TACKLE SENATOR", "GELOMBANG NANO", "LEDAKAN NANOMESIN"], "skills": {"skill1": {"kind": "dash", "damage": 30, "dash": 900, "reach": 160, "knockback": 420, "duration": 0.6, "hitAt": 0.55, "asset": "tackle", "size": 300}, "skill2": {"kind": "wave", "damage": 24, "speed": 760, "range": 620, "size": 330, "tall": 210, "knockback": 260, "duration": 0.62, "hitAt": 0.5, "asset": "nano", "pierce": true}}, "ultimate": {"kind": "rain", "asset": "nanoblast", "count": 18, "gap": 0.1, "damage": 5, "radius": 125, "spread": 520, "size": 180, "delay": 0.4, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 40, "asset": "burst"}}, "cutin": {"a": "#0a1020", "b": "#305a9e", "kicker": "#e0efff", "title": "LEDAKAN
NANOMESIN", "detail": "THE SENATOR IS UNLEASHED"}, "form": {"manifest": {"characterId": "anice", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -70, "right": 68, "top": -184, "bottom": 0}, "height": 184}, {"frame": 1, "bounds": {"left": -69, "right": 65, "top": -176, "bottom": 0}, "height": 176}, {"frame": 2, "bounds": {"left": -69, "right": 68, "top": -184, "bottom": 0}, "height": 184}, {"frame": 3, "bounds": {"left": -69, "right": 66, "top": -177, "bottom": 0}, "height": 177}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -71, "right": 68, "top": -193, "bottom": 0}, "height": 193}, {"frame": 1, "bounds": {"left": -35, "right": 62, "top": -195, "bottom": 0}, "height": 195}, {"frame": 2, "bounds": {"left": -74, "right": 71, "top": -192, "bottom": 0}, "height": 192}, {"frame": 3, "bounds": {"left": -47, "right": 64, "top": -195, "bottom": 0}, "height": 195}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -99, "right": 39, "top": -149, "bottom": 0}, "height": 149}, {"frame": 1, "bounds": {"left": -23, "right": 116, "top": -152, "bottom": 0}, "height": 152}, {"frame": 2, "bounds": {"left": -40, "right": 110, "top": -152, "bottom": 0}, "height": 152}, {"frame": 3, "bounds": {"left": -34, "right": 116, "top": -149, "bottom": 0}, "height": 149}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -25, "right": 118, "top": -167, "bottom": 0}, "height": 167}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -39, "right": 52, "top": -119, "bottom": 0}, "height": 119}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 69, "top": -140, "bottom": 0}, "height": 140}, {"frame": 1, "bounds": {"left": -54, "right": 70, "top": -146, "bottom": 0}, "height": 146}, {"frame": 2, "bounds": {"left": -56, "right": 84, "top": -144, "bottom": 0}, "height": 144}, {"frame": 3, "bounds": {"left": -53, "right": 69, "top": -149, "bottom": 0}, "height": 149}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -65, "right": 65, "top": -156, "bottom": 0}, "height": 156}, {"frame": 1, "bounds": {"left": -64, "right": 101, "top": -157, "bottom": 0}, "height": 157}, {"frame": 2, "bounds": {"left": -72, "right": 126, "top": -157, "bottom": 0}, "height": 157}, {"frame": 3, "bounds": {"left": -69, "right": 69, "top": -157, "bottom": 0}, "height": 157}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -68, "right": 67, "top": -153, "bottom": 0}, "height": 153}, {"frame": 1, "bounds": {"left": -64, "right": 92, "top": -151, "bottom": 0}, "height": 151}, {"frame": 2, "bounds": {"left": -71, "right": 104, "top": -141, "bottom": 0}, "height": 141}, {"frame": 3, "bounds": {"left": -69, "right": 66, "top": -151, "bottom": 0}, "height": 151}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -66, "right": 69, "top": -190, "bottom": 0}, "height": 190}, {"frame": 1, "bounds": {"left": -43, "right": 50, "top": -152, "bottom": 0}, "height": 152}, {"frame": 2, "bounds": {"left": -73, "right": 70, "top": -122, "bottom": 0}, "height": 122}, {"frame": 3, "bounds": {"left": -64, "right": 67, "top": -171, "bottom": 0}, "height": 171}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -66, "right": 58, "top": -110, "bottom": 0}, "height": 110}, {"frame": 1, "bounds": {"left": -76, "right": 61, "top": -132, "bottom": 0}, "height": 132}, {"frame": 2, "bounds": {"left": -27, "right": 143, "top": -124, "bottom": 0}, "height": 124}, {"frame": 3, "bounds": {"left": -61, "right": 65, "top": -148, "bottom": 0}, "height": 148}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -68, "right": 67, "top": -165, "bottom": 0}, "height": 165}, {"frame": 1, "bounds": {"left": -67, "right": 95, "top": -166, "bottom": 0}, "height": 166}, {"frame": 2, "bounds": {"left": -78, "right": 83, "top": -166, "bottom": 0}, "height": 166}, {"frame": 3, "bounds": {"left": -73, "right": 71, "top": -166, "bottom": 0}, "height": 166}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -67, "right": 68, "top": -136, "bottom": 0}, "height": 136}, {"frame": 1, "bounds": {"left": -81, "right": 86, "top": -183, "bottom": 0}, "height": 183}, {"frame": 2, "bounds": {"left": -70, "right": 71, "top": -162, "bottom": 0}, "height": 162}, {"frame": 3, "bounds": {"left": -70, "right": 72, "top": -167, "bottom": 0}, "height": 167}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -75, "right": 71, "top": -172, "bottom": 0}, "height": 172}, {"frame": 1, "bounds": {"left": -91, "right": 62, "top": -167, "bottom": 0}, "height": 167}, {"frame": 2, "bounds": {"left": -66, "right": 71, "top": -158, "bottom": 0}, "height": 158}, {"frame": 3, "bounds": {"left": -70, "right": 70, "top": -172, "bottom": 0}, "height": 172}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 75, "top": -130, "bottom": 0}, "height": 130}, {"frame": 1, "bounds": {"left": -102, "right": 31, "top": -101, "bottom": 0}, "height": 101}, {"frame": 2, "bounds": {"left": -48, "right": 87, "top": -90, "bottom": 0}, "height": 90}, {"frame": 3, "bounds": {"left": -58, "right": 97, "top": -42, "bottom": 0}, "height": 42}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -84, "right": 81, "top": -74, "bottom": 0}, "height": 74}, {"frame": 1, "bounds": {"left": -74, "right": 67, "top": -95, "bottom": 0}, "height": 95}, {"frame": 2, "bounds": {"left": -52, "right": 61, "top": -115, "bottom": 0}, "height": 115}, {"frame": 3, "bounds": {"left": -65, "right": 63, "top": -156, "bottom": 0}, "height": 156}]}}, "emitters": {"attack1": {"x": 126, "y": -124, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 104, "y": -116, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 70, "y": -9, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 143, "y": -62, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 83, "y": -128, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 71, "y": -8, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 46, "y": -84}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 6, "y": -60}}, "measurement": "Measured from the final atlas; no runtime resizing."}}}}};
+window.FIGHTERS.anice={"id": "anice", "name": "A NICE", "tag": "THE NARRATOR", "race": "HUMAN", "title": "NARRATOR", "color": "#5aa9f0", "status": "ON THE PODIUM", "select": "assets/menu/anice-select.webp", "detail": "Polite, precise, and every sentence lands.", "style": "Zoner / words and podium", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["JAB NARASI", "GAGASAN BEAM", "MIMBAR SLAM", "PIDATO PANJANG"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "projectile", "damage": 14, "speed": 820, "range": 580, "size": 170, "knockback": 180, "duration": 0.6, "hitAt": 0.5, "asset": "words"}, "skill2": {"kind": "area", "damage": 20, "ahead": 110, "radius": 115, "vy": 140, "knockback": 260, "duration": 0.62, "hitAt": 0.55, "asset": "podium"}}, "ultimate": {"kind": "volley", "asset": "words", "count": 16, "gap": 0.09, "damage": 3, "speed": 950, "size": 110, "homing": true, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 26, "asset": "burst"}}, "fx": ["burst", "impact", "nano", "nanoblast", "podium", "tackle", "words"], "cutin": {"a": "#0a1830", "b": "#245a9e", "kicker": "#d8ecff", "title": "PIDATO
PANJANG", "detail": "A STORM OF WORDS"}, "manifest": {"characterId": "anice", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "green", "hex": "#00FF00", "rgb": [0, 255, 0], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -58, "right": 61, "top": -177, "bottom": 0}, "height": 177}, {"frame": 1, "bounds": {"left": -57, "right": 61, "top": -185, "bottom": 0}, "height": 185}, {"frame": 2, "bounds": {"left": -58, "right": 60, "top": -176, "bottom": 0}, "height": 176}, {"frame": 3, "bounds": {"left": -58, "right": 60, "top": -184, "bottom": 0}, "height": 184}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -57, "right": 61, "top": -203, "bottom": 0}, "height": 203}, {"frame": 1, "bounds": {"left": -33, "right": 55, "top": -202, "bottom": 0}, "height": 202}, {"frame": 2, "bounds": {"left": -65, "right": 67, "top": -203, "bottom": 0}, "height": 203}, {"frame": 3, "bounds": {"left": -56, "right": 49, "top": -202, "bottom": 0}, "height": 202}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -90, "right": 30, "top": -166, "bottom": 0}, "height": 166}, {"frame": 1, "bounds": {"left": -25, "right": 75, "top": -166, "bottom": 0}, "height": 166}, {"frame": 2, "bounds": {"left": -98, "right": 37, "top": -164, "bottom": 0}, "height": 164}, {"frame": 3, "bounds": {"left": -24, "right": 83, "top": -167, "bottom": 0}, "height": 167}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -20, "right": 77, "top": -154, "bottom": 0}, "height": 154}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -27, "right": 29, "top": -112, "bottom": 0}, "height": 112}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -56, "right": 65, "top": -156, "bottom": 0}, "height": 156}, {"frame": 1, "bounds": {"left": -50, "right": 59, "top": -137, "bottom": 0}, "height": 137}, {"frame": 2, "bounds": {"left": -57, "right": 62, "top": -150, "bottom": 0}, "height": 150}, {"frame": 3, "bounds": {"left": -56, "right": 61, "top": -163, "bottom": 0}, "height": 163}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -52, "right": 58, "top": -172, "bottom": 0}, "height": 172}, {"frame": 1, "bounds": {"left": -58, "right": 77, "top": -172, "bottom": 0}, "height": 172}, {"frame": 2, "bounds": {"left": -60, "right": 92, "top": -171, "bottom": 0}, "height": 171}, {"frame": 3, "bounds": {"left": -57, "right": 61, "top": -172, "bottom": 0}, "height": 172}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -69, "right": 66, "top": -186, "bottom": 0}, "height": 186}, {"frame": 1, "bounds": {"left": -65, "right": 68, "top": -189, "bottom": 0}, "height": 189}, {"frame": 2, "bounds": {"left": -67, "right": 96, "top": -191, "bottom": 0}, "height": 191}, {"frame": 3, "bounds": {"left": -66, "right": 67, "top": -193, "bottom": 0}, "height": 193}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 63, "top": -171, "bottom": 0}, "height": 171}, {"frame": 1, "bounds": {"left": -64, "right": 87, "top": -172, "bottom": 0}, "height": 172}, {"frame": 2, "bounds": {"left": -73, "right": 105, "top": -173, "bottom": 0}, "height": 173}, {"frame": 3, "bounds": {"left": -60, "right": 62, "top": -189, "bottom": 0}, "height": 189}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 61, "top": -193, "bottom": 0}, "height": 193}, {"frame": 1, "bounds": {"left": -59, "right": 73, "top": -194, "bottom": 0}, "height": 194}, {"frame": 2, "bounds": {"left": -60, "right": 98, "top": -187, "bottom": 0}, "height": 187}, {"frame": 3, "bounds": {"left": -56, "right": 63, "top": -193, "bottom": 0}, "height": 193}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 63, "top": -186, "bottom": 0}, "height": 186}, {"frame": 1, "bounds": {"left": -64, "right": 65, "top": -170, "bottom": 0}, "height": 170}, {"frame": 2, "bounds": {"left": -67, "right": 65, "top": -159, "bottom": 0}, "height": 159}, {"frame": 3, "bounds": {"left": -67, "right": 67, "top": -182, "bottom": 0}, "height": 182}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -46, "right": 49, "top": -184, "bottom": 0}, "height": 184}, {"frame": 1, "bounds": {"left": -75, "right": 78, "top": -184, "bottom": 0}, "height": 184}, {"frame": 2, "bounds": {"left": -60, "right": 80, "top": -181, "bottom": 0}, "height": 181}, {"frame": 3, "bounds": {"left": -60, "right": 81, "top": -182, "bottom": 0}, "height": 182}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 66, "top": -175, "bottom": 0}, "height": 175}, {"frame": 1, "bounds": {"left": -54, "right": 65, "top": -192, "bottom": 0}, "height": 192}, {"frame": 2, "bounds": {"left": -59, "right": 66, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -65, "right": 67, "top": -194, "bottom": 0}, "height": 194}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -58, "right": 46, "top": -137, "bottom": 0}, "height": 137}, {"frame": 1, "bounds": {"left": -104, "right": 22, "top": -124, "bottom": 0}, "height": 124}, {"frame": 2, "bounds": {"left": -74, "right": 69, "top": -60, "bottom": 0}, "height": 60}, {"frame": 3, "bounds": {"left": -73, "right": 77, "top": -37, "bottom": 0}, "height": 37}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -86, "right": 70, "top": -64, "bottom": 0}, "height": 64}, {"frame": 1, "bounds": {"left": -71, "right": 62, "top": -102, "bottom": 0}, "height": 102}, {"frame": 2, "bounds": {"left": -46, "right": 59, "top": -130, "bottom": 0}, "height": 130}, {"frame": 3, "bounds": {"left": -63, "right": 63, "top": -184, "bottom": 0}, "height": 184}]}}, "emitters": {"attack1": {"x": 92, "y": -127, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 96, "y": -142, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 105, "y": -127, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 98, "y": -152, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 65, "y": -6, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 80, "y": -145, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 28, "y": -77}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 1, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "SENATOR", "label": "SENATOR", "announce": "MODE SENATOR!", "duration": 10, "damageMul": 1.5, "speedMul": 1.1, "reachAdd": 30, "aura": "#ffd24a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["TINJU SENATOR", "TACKLE SENATOR", "GELOMBANG NANO", "LEDAKAN NANOMESIN"], "skills": {"skill1": {"kind": "dash", "damage": 30, "dash": 900, "reach": 160, "knockback": 420, "duration": 0.6, "hitAt": 0.55, "asset": "tackle", "size": 300}, "skill2": {"kind": "wave", "damage": 24, "speed": 760, "range": 620, "size": 330, "tall": 210, "knockback": 260, "duration": 0.62, "hitAt": 0.5, "asset": "nano", "pierce": true, "split": {"at": 230, "dmg": 0.5}}}, "ultimate": {"kind": "rain", "asset": "nanoblast", "count": 18, "gap": 0.1, "damage": 5, "radius": 125, "spread": 520, "size": 180, "delay": 0.4, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 40, "asset": "burst"}}, "cutin": {"a": "#0a1020", "b": "#305a9e", "kicker": "#e0efff", "title": "LEDAKAN
NANOMESIN", "detail": "THE SENATOR IS UNLEASHED"}, "form": {"manifest": {"characterId": "anice", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -70, "right": 68, "top": -184, "bottom": 0}, "height": 184}, {"frame": 1, "bounds": {"left": -69, "right": 65, "top": -176, "bottom": 0}, "height": 176}, {"frame": 2, "bounds": {"left": -69, "right": 68, "top": -184, "bottom": 0}, "height": 184}, {"frame": 3, "bounds": {"left": -69, "right": 66, "top": -177, "bottom": 0}, "height": 177}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -71, "right": 68, "top": -193, "bottom": 0}, "height": 193}, {"frame": 1, "bounds": {"left": -35, "right": 62, "top": -195, "bottom": 0}, "height": 195}, {"frame": 2, "bounds": {"left": -74, "right": 71, "top": -192, "bottom": 0}, "height": 192}, {"frame": 3, "bounds": {"left": -47, "right": 64, "top": -195, "bottom": 0}, "height": 195}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -99, "right": 39, "top": -149, "bottom": 0}, "height": 149}, {"frame": 1, "bounds": {"left": -23, "right": 116, "top": -152, "bottom": 0}, "height": 152}, {"frame": 2, "bounds": {"left": -40, "right": 110, "top": -152, "bottom": 0}, "height": 152}, {"frame": 3, "bounds": {"left": -34, "right": 116, "top": -149, "bottom": 0}, "height": 149}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -25, "right": 118, "top": -167, "bottom": 0}, "height": 167}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -39, "right": 52, "top": -119, "bottom": 0}, "height": 119}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 69, "top": -140, "bottom": 0}, "height": 140}, {"frame": 1, "bounds": {"left": -54, "right": 70, "top": -146, "bottom": 0}, "height": 146}, {"frame": 2, "bounds": {"left": -56, "right": 84, "top": -144, "bottom": 0}, "height": 144}, {"frame": 3, "bounds": {"left": -53, "right": 69, "top": -149, "bottom": 0}, "height": 149}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -65, "right": 65, "top": -156, "bottom": 0}, "height": 156}, {"frame": 1, "bounds": {"left": -64, "right": 101, "top": -157, "bottom": 0}, "height": 157}, {"frame": 2, "bounds": {"left": -72, "right": 126, "top": -157, "bottom": 0}, "height": 157}, {"frame": 3, "bounds": {"left": -69, "right": 69, "top": -157, "bottom": 0}, "height": 157}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -68, "right": 67, "top": -153, "bottom": 0}, "height": 153}, {"frame": 1, "bounds": {"left": -64, "right": 92, "top": -151, "bottom": 0}, "height": 151}, {"frame": 2, "bounds": {"left": -71, "right": 104, "top": -141, "bottom": 0}, "height": 141}, {"frame": 3, "bounds": {"left": -69, "right": 66, "top": -151, "bottom": 0}, "height": 151}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -66, "right": 69, "top": -190, "bottom": 0}, "height": 190}, {"frame": 1, "bounds": {"left": -43, "right": 50, "top": -152, "bottom": 0}, "height": 152}, {"frame": 2, "bounds": {"left": -73, "right": 70, "top": -122, "bottom": 0}, "height": 122}, {"frame": 3, "bounds": {"left": -64, "right": 67, "top": -171, "bottom": 0}, "height": 171}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -66, "right": 58, "top": -110, "bottom": 0}, "height": 110}, {"frame": 1, "bounds": {"left": -76, "right": 61, "top": -132, "bottom": 0}, "height": 132}, {"frame": 2, "bounds": {"left": -27, "right": 143, "top": -124, "bottom": 0}, "height": 124}, {"frame": 3, "bounds": {"left": -61, "right": 65, "top": -148, "bottom": 0}, "height": 148}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -68, "right": 67, "top": -165, "bottom": 0}, "height": 165}, {"frame": 1, "bounds": {"left": -67, "right": 95, "top": -166, "bottom": 0}, "height": 166}, {"frame": 2, "bounds": {"left": -78, "right": 83, "top": -166, "bottom": 0}, "height": 166}, {"frame": 3, "bounds": {"left": -73, "right": 71, "top": -166, "bottom": 0}, "height": 166}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -67, "right": 68, "top": -136, "bottom": 0}, "height": 136}, {"frame": 1, "bounds": {"left": -81, "right": 86, "top": -183, "bottom": 0}, "height": 183}, {"frame": 2, "bounds": {"left": -70, "right": 71, "top": -162, "bottom": 0}, "height": 162}, {"frame": 3, "bounds": {"left": -70, "right": 72, "top": -167, "bottom": 0}, "height": 167}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -75, "right": 71, "top": -172, "bottom": 0}, "height": 172}, {"frame": 1, "bounds": {"left": -91, "right": 62, "top": -167, "bottom": 0}, "height": 167}, {"frame": 2, "bounds": {"left": -66, "right": 71, "top": -158, "bottom": 0}, "height": 158}, {"frame": 3, "bounds": {"left": -70, "right": 70, "top": -172, "bottom": 0}, "height": 172}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 75, "top": -130, "bottom": 0}, "height": 130}, {"frame": 1, "bounds": {"left": -102, "right": 31, "top": -101, "bottom": 0}, "height": 101}, {"frame": 2, "bounds": {"left": -48, "right": 87, "top": -90, "bottom": 0}, "height": 90}, {"frame": 3, "bounds": {"left": -58, "right": 97, "top": -42, "bottom": 0}, "height": 42}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -84, "right": 81, "top": -74, "bottom": 0}, "height": 74}, {"frame": 1, "bounds": {"left": -74, "right": 67, "top": -95, "bottom": 0}, "height": 95}, {"frame": 2, "bounds": {"left": -52, "right": 61, "top": -115, "bottom": 0}, "height": 115}, {"frame": 3, "bounds": {"left": -65, "right": 63, "top": -156, "bottom": 0}, "height": 156}]}}, "emitters": {"attack1": {"x": 126, "y": -124, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 104, "y": -116, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 70, "y": -9, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 143, "y": -62, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 83, "y": -128, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 71, "y": -8, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 46, "y": -84}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 6, "y": -60}}, "measurement": "Measured from the final atlas; no runtime resizing."}}}}};
diff --git a/assets/ethanolman/manifest.js b/assets/ethanolman/manifest.js
index 5a01a9f..4c2e083 100644
--- a/assets/ethanolman/manifest.js
+++ b/assets/ethanolman/manifest.js
@@ -1,2 +1,2 @@
window.FIGHTERS=window.FIGHTERS||{};
-window.FIGHTERS.ethanolman={"id": "ethanolman", "name": "ETHANOL MAN", "tag": "THE GREEN FUEL", "race": "HUMAN", "title": "GREEN FUEL", "color": "#2fbf7a", "status": "FULLY FUELED", "select": "assets/menu/ethanolman-select.webp", "detail": "Cheerful, all in white, and a flood of bio-fuel.", "style": "Heavy zoner / waves and area", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["TINJU ETANOL", "ETHANOL SPLASH", "TAWA MELEDAK", "BANJIR BIOETANOL"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "wave", "damage": 17, "speed": 620, "range": 520, "size": 280, "tall": 190, "knockback": 220, "duration": 0.62, "hitAt": 0.5, "asset": "wave", "pierce": true}, "skill2": {"kind": "area", "damage": 18, "ahead": 0, "radius": 135, "vy": 140, "knockback": 260, "duration": 0.6, "hitAt": 0.5, "asset": "splash"}}, "ultimate": {"kind": "stampede", "asset": "flood", "count": 5, "gap": 0.5, "damage": 9, "speed": 900, "size": 420, "lift": 110, "rx": 120, "ry": 150, "hold": 1.2, "settle": 0.7, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["blast", "burst", "fireflow", "firepillar", "flood", "impact", "splash", "wave"], "cutin": {"a": "#06231a", "b": "#12704f", "kicker": "#c8ffe8", "title": "BANJIR
BIOETANOL", "detail": "FLOOD OF GREEN FUEL"}, "manifest": {"characterId": "ethanolman", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -61, "right": 54, "top": -180, "bottom": 0}, "height": 180}, {"frame": 1, "bounds": {"left": -59, "right": 53, "top": -179, "bottom": 0}, "height": 179}, {"frame": 2, "bounds": {"left": -60, "right": 53, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -60, "right": 53, "top": -180, "bottom": 0}, "height": 180}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 48, "top": -164, "bottom": 0}, "height": 164}, {"frame": 1, "bounds": {"left": -27, "right": 44, "top": -164, "bottom": 0}, "height": 164}, {"frame": 2, "bounds": {"left": -47, "right": 49, "top": -164, "bottom": 0}, "height": 164}, {"frame": 3, "bounds": {"left": -41, "right": 49, "top": -164, "bottom": 0}, "height": 164}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -80, "right": 35, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -26, "right": 65, "top": -140, "bottom": 0}, "height": 140}, {"frame": 2, "bounds": {"left": -75, "right": 52, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -19, "right": 84, "top": -146, "bottom": 0}, "height": 146}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -25, "right": 81, "top": -150, "bottom": 0}, "height": 150}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -33, "right": 32, "top": -115, "bottom": 0}, "height": 115}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 56, "top": -143, "bottom": 0}, "height": 143}, {"frame": 1, "bounds": {"left": -50, "right": 53, "top": -122, "bottom": 0}, "height": 122}, {"frame": 2, "bounds": {"left": -47, "right": 53, "top": -121, "bottom": 0}, "height": 121}, {"frame": 3, "bounds": {"left": -48, "right": 50, "top": -136, "bottom": 0}, "height": 136}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 52, "top": -154, "bottom": 0}, "height": 154}, {"frame": 1, "bounds": {"left": -50, "right": 76, "top": -153, "bottom": 0}, "height": 153}, {"frame": 2, "bounds": {"left": -57, "right": 91, "top": -153, "bottom": 0}, "height": 153}, {"frame": 3, "bounds": {"left": -52, "right": 52, "top": -155, "bottom": 0}, "height": 155}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 53, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -53, "right": 67, "top": -145, "bottom": 0}, "height": 145}, {"frame": 2, "bounds": {"left": -63, "right": 78, "top": -144, "bottom": 0}, "height": 144}, {"frame": 3, "bounds": {"left": -55, "right": 51, "top": -147, "bottom": 0}, "height": 147}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -56, "right": 52, "top": -140, "bottom": 0}, "height": 140}, {"frame": 1, "bounds": {"left": -62, "right": 62, "top": -141, "bottom": 0}, "height": 141}, {"frame": 2, "bounds": {"left": -67, "right": 73, "top": -139, "bottom": 0}, "height": 139}, {"frame": 3, "bounds": {"left": -53, "right": 50, "top": -147, "bottom": 0}, "height": 147}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 50, "top": -142, "bottom": 0}, "height": 142}, {"frame": 1, "bounds": {"left": -56, "right": 55, "top": -146, "bottom": 0}, "height": 146}, {"frame": 2, "bounds": {"left": -56, "right": 79, "top": -147, "bottom": 0}, "height": 147}, {"frame": 3, "bounds": {"left": -59, "right": 55, "top": -147, "bottom": 0}, "height": 147}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -45, "right": 47, "top": -130, "bottom": 0}, "height": 130}, {"frame": 1, "bounds": {"left": -65, "right": 60, "top": -122, "bottom": 0}, "height": 122}, {"frame": 2, "bounds": {"left": -72, "right": 59, "top": -141, "bottom": 0}, "height": 141}, {"frame": 3, "bounds": {"left": -50, "right": 49, "top": -136, "bottom": 0}, "height": 136}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 46, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -54, "right": 49, "top": -167, "bottom": 0}, "height": 167}, {"frame": 2, "bounds": {"left": -55, "right": 52, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -55, "right": 54, "top": -183, "bottom": 0}, "height": 183}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 56, "top": -152, "bottom": 0}, "height": 152}, {"frame": 1, "bounds": {"left": -67, "right": 58, "top": -158, "bottom": 0}, "height": 158}, {"frame": 2, "bounds": {"left": -50, "right": 55, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -54, "right": 56, "top": -158, "bottom": 0}, "height": 158}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 61, "top": -111, "bottom": 0}, "height": 111}, {"frame": 1, "bounds": {"left": -87, "right": 38, "top": -94, "bottom": 0}, "height": 94}, {"frame": 2, "bounds": {"left": -70, "right": 61, "top": -56, "bottom": 0}, "height": 56}, {"frame": 3, "bounds": {"left": -56, "right": 72, "top": -35, "bottom": 0}, "height": 35}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -71, "right": 76, "top": -60, "bottom": 0}, "height": 60}, {"frame": 1, "bounds": {"left": -66, "right": 55, "top": -96, "bottom": 0}, "height": 96}, {"frame": 2, "bounds": {"left": -38, "right": 52, "top": -110, "bottom": 0}, "height": 110}, {"frame": 3, "bounds": {"left": -55, "right": 50, "top": -141, "bottom": 0}, "height": 141}]}}, "emitters": {"attack1": {"x": 91, "y": -112, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 78, "y": -108, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 73, "y": -103, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 79, "y": -112, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 59, "y": -99, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 52, "y": -5, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 28, "y": -75}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": -1, "y": -58}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE LEDAKAN", "label": "LEDAKAN", "announce": "MODE LEDAKAN!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#ff9a2a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["TINJU API", "PERCIKAN API", "BOTOL MELEDAK", "LEDAKAN BIOETANOL"], "skills": {"skill1": {"kind": "wave", "damage": 22, "speed": 680, "range": 580, "size": 320, "tall": 200, "knockback": 240, "duration": 0.62, "hitAt": 0.5, "asset": "fireflow", "pierce": true}, "skill2": {"kind": "area", "damage": 28, "ahead": 0, "radius": 180, "vy": 150, "knockback": 320, "duration": 0.6, "hitAt": 0.5, "asset": "blast"}}, "ultimate": {"kind": "pillars", "asset": "firepillar", "count": 12, "gap": 0.12, "damage": 4, "h": 380, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 32, "asset": "burst"}}, "cutin": {"a": "#1f0d00", "b": "#c46a10", "kicker": "#ffe8c0", "title": "LEDAKAN
BIOETANOL", "detail": "FUEL MEETS FIRE"}}};
+window.FIGHTERS.ethanolman={"id": "ethanolman", "name": "ETHANOL MAN", "tag": "THE GREEN FUEL", "race": "HUMAN", "title": "GREEN FUEL", "color": "#2fbf7a", "status": "FULLY FUELED", "select": "assets/menu/ethanolman-select.webp", "detail": "Cheerful, all in white, and a flood of bio-fuel.", "style": "Heavy zoner / waves and area", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["TINJU ETANOL", "ETHANOL SPLASH", "TAWA MELEDAK", "BANJIR BIOETANOL"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "wave", "damage": 17, "speed": 620, "range": 520, "size": 280, "tall": 190, "knockback": 220, "duration": 0.62, "hitAt": 0.5, "asset": "wave", "pierce": true, "fx": "roll", "lift": 620}, "skill2": {"kind": "area", "damage": 18, "ahead": 0, "radius": 135, "vy": 140, "knockback": 260, "duration": 0.6, "hitAt": 0.5, "asset": "splash"}}, "ultimate": {"kind": "stampede", "asset": "flood", "count": 5, "gap": 0.5, "damage": 9, "speed": 900, "size": 420, "lift": 110, "rx": 120, "ry": 150, "hold": 1.2, "settle": 0.7, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["blast", "burst", "fireflow", "firepillar", "flood", "impact", "splash", "wave"], "cutin": {"a": "#06231a", "b": "#12704f", "kicker": "#c8ffe8", "title": "BANJIR
BIOETANOL", "detail": "FLOOD OF GREEN FUEL"}, "manifest": {"characterId": "ethanolman", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -61, "right": 54, "top": -180, "bottom": 0}, "height": 180}, {"frame": 1, "bounds": {"left": -59, "right": 53, "top": -179, "bottom": 0}, "height": 179}, {"frame": 2, "bounds": {"left": -60, "right": 53, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -60, "right": 53, "top": -180, "bottom": 0}, "height": 180}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 48, "top": -164, "bottom": 0}, "height": 164}, {"frame": 1, "bounds": {"left": -27, "right": 44, "top": -164, "bottom": 0}, "height": 164}, {"frame": 2, "bounds": {"left": -47, "right": 49, "top": -164, "bottom": 0}, "height": 164}, {"frame": 3, "bounds": {"left": -41, "right": 49, "top": -164, "bottom": 0}, "height": 164}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -80, "right": 35, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -26, "right": 65, "top": -140, "bottom": 0}, "height": 140}, {"frame": 2, "bounds": {"left": -75, "right": 52, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -19, "right": 84, "top": -146, "bottom": 0}, "height": 146}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -25, "right": 81, "top": -150, "bottom": 0}, "height": 150}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -33, "right": 32, "top": -115, "bottom": 0}, "height": 115}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 56, "top": -143, "bottom": 0}, "height": 143}, {"frame": 1, "bounds": {"left": -50, "right": 53, "top": -122, "bottom": 0}, "height": 122}, {"frame": 2, "bounds": {"left": -47, "right": 53, "top": -121, "bottom": 0}, "height": 121}, {"frame": 3, "bounds": {"left": -48, "right": 50, "top": -136, "bottom": 0}, "height": 136}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 52, "top": -154, "bottom": 0}, "height": 154}, {"frame": 1, "bounds": {"left": -50, "right": 76, "top": -153, "bottom": 0}, "height": 153}, {"frame": 2, "bounds": {"left": -57, "right": 91, "top": -153, "bottom": 0}, "height": 153}, {"frame": 3, "bounds": {"left": -52, "right": 52, "top": -155, "bottom": 0}, "height": 155}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 53, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -53, "right": 67, "top": -145, "bottom": 0}, "height": 145}, {"frame": 2, "bounds": {"left": -63, "right": 78, "top": -144, "bottom": 0}, "height": 144}, {"frame": 3, "bounds": {"left": -55, "right": 51, "top": -147, "bottom": 0}, "height": 147}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -56, "right": 52, "top": -140, "bottom": 0}, "height": 140}, {"frame": 1, "bounds": {"left": -62, "right": 62, "top": -141, "bottom": 0}, "height": 141}, {"frame": 2, "bounds": {"left": -67, "right": 73, "top": -139, "bottom": 0}, "height": 139}, {"frame": 3, "bounds": {"left": -53, "right": 50, "top": -147, "bottom": 0}, "height": 147}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 50, "top": -142, "bottom": 0}, "height": 142}, {"frame": 1, "bounds": {"left": -56, "right": 55, "top": -146, "bottom": 0}, "height": 146}, {"frame": 2, "bounds": {"left": -56, "right": 79, "top": -147, "bottom": 0}, "height": 147}, {"frame": 3, "bounds": {"left": -59, "right": 55, "top": -147, "bottom": 0}, "height": 147}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -45, "right": 47, "top": -130, "bottom": 0}, "height": 130}, {"frame": 1, "bounds": {"left": -65, "right": 60, "top": -122, "bottom": 0}, "height": 122}, {"frame": 2, "bounds": {"left": -72, "right": 59, "top": -141, "bottom": 0}, "height": 141}, {"frame": 3, "bounds": {"left": -50, "right": 49, "top": -136, "bottom": 0}, "height": 136}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 46, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -54, "right": 49, "top": -167, "bottom": 0}, "height": 167}, {"frame": 2, "bounds": {"left": -55, "right": 52, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -55, "right": 54, "top": -183, "bottom": 0}, "height": 183}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 56, "top": -152, "bottom": 0}, "height": 152}, {"frame": 1, "bounds": {"left": -67, "right": 58, "top": -158, "bottom": 0}, "height": 158}, {"frame": 2, "bounds": {"left": -50, "right": 55, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -54, "right": 56, "top": -158, "bottom": 0}, "height": 158}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 61, "top": -111, "bottom": 0}, "height": 111}, {"frame": 1, "bounds": {"left": -87, "right": 38, "top": -94, "bottom": 0}, "height": 94}, {"frame": 2, "bounds": {"left": -70, "right": 61, "top": -56, "bottom": 0}, "height": 56}, {"frame": 3, "bounds": {"left": -56, "right": 72, "top": -35, "bottom": 0}, "height": 35}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -71, "right": 76, "top": -60, "bottom": 0}, "height": 60}, {"frame": 1, "bounds": {"left": -66, "right": 55, "top": -96, "bottom": 0}, "height": 96}, {"frame": 2, "bounds": {"left": -38, "right": 52, "top": -110, "bottom": 0}, "height": 110}, {"frame": 3, "bounds": {"left": -55, "right": 50, "top": -141, "bottom": 0}, "height": 141}]}}, "emitters": {"attack1": {"x": 91, "y": -112, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 78, "y": -108, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 73, "y": -103, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 79, "y": -112, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 59, "y": -99, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 52, "y": -5, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 28, "y": -75}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": -1, "y": -58}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE LEDAKAN", "label": "LEDAKAN", "announce": "MODE LEDAKAN!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#ff9a2a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["TINJU API", "PERCIKAN API", "BOTOL MELEDAK", "LEDAKAN BIOETANOL"], "skills": {"skill1": {"kind": "wave", "damage": 20, "speed": 680, "range": 580, "size": 320, "tall": 200, "knockback": 240, "duration": 0.62, "hitAt": 0.5, "asset": "fireflow", "pierce": true, "fx": "roll", "fire": true, "trail": {"gap": 115, "radius": 60, "dps": 3, "life": 3}}, "skill2": {"kind": "area", "damage": 22, "ahead": 0, "radius": 180, "vy": 150, "knockback": 320, "duration": 0.6, "hitAt": 0.5, "asset": "blast", "aftershock": {"delay": 0.22, "radius": 260, "damage": 9}}}, "ultimate": {"kind": "pillars", "asset": "firepillar", "count": 12, "gap": 0.12, "damage": 4, "h": 380, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 32, "asset": "burst"}}, "cutin": {"a": "#1f0d00", "b": "#c46a10", "kicker": "#ffe8c0", "title": "LEDAKAN
BIOETANOL", "detail": "FUEL MEETS FIRE"}}};
diff --git a/assets/gufron/manifest.js b/assets/gufron/manifest.js
index cd57321..3e1352e 100644
--- a/assets/gufron/manifest.js
+++ b/assets/gufron/manifest.js
@@ -1,2 +1,2 @@
window.FIGHTERS=window.FIGHTERS||{};
-window.FIGHTERS.gufron={"id": "gufron", "name": "GUFRON", "tag": "THE ANT WHISPERER", "race": "HUMAN", "title": "ANT WHISPERER", "color": "#c9a05a", "status": "ONE WITH THE ANTS", "select": "assets/menu/gufron-select.webp", "detail": "Quirky, calm, and followed by a very large colony.", "style": "Swarm / ants and sound", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["JAB NYENTRIK", "BAHASA SEMUT", "SYUDUDU", "KOLONI SEMUT RAYA"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "homing", "damage": 4, "count": 6, "speed": 520, "turn": 5, "spread": 60, "size": 70, "knockback": 90, "duration": 0.6, "hitAt": 0.5, "asset": "ant", "life": 2.4}, "skill2": {"kind": "wave", "damage": 15, "speed": 640, "range": 520, "size": 220, "tall": 170, "knockback": 200, "duration": 0.62, "hitAt": 0.5, "asset": "soundwave", "pierce": true}}, "ultimate": {"kind": "stampede", "asset": "antswarm", "count": 9, "gap": 0.24, "damage": 5, "speed": 900, "size": 280, "lift": 60, "rx": 100, "ry": 110, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 26, "asset": "burst"}}, "fx": ["ant", "antswarm", "burst", "giantant", "impact", "soundchain", "soundwave"], "cutin": {"a": "#1c1405", "b": "#5a4318", "kicker": "#ffe9b8", "title": "KOLONI
SEMUT RAYA", "detail": "THE COLONY ARRIVES"}, "manifest": {"characterId": "gufron", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 51, "top": -181, "bottom": 0}, "height": 181}, {"frame": 1, "bounds": {"left": -47, "right": 50, "top": -181, "bottom": 0}, "height": 181}, {"frame": 2, "bounds": {"left": -47, "right": 50, "top": -180, "bottom": 0}, "height": 180}, {"frame": 3, "bounds": {"left": -47, "right": 50, "top": -181, "bottom": 0}, "height": 181}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -45, "right": 47, "top": -156, "bottom": 0}, "height": 156}, {"frame": 1, "bounds": {"left": -26, "right": 47, "top": -156, "bottom": 0}, "height": 156}, {"frame": 2, "bounds": {"left": -45, "right": 48, "top": -156, "bottom": 0}, "height": 156}, {"frame": 3, "bounds": {"left": -33, "right": 42, "top": -156, "bottom": 0}, "height": 156}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -81, "right": 50, "top": -137, "bottom": 0}, "height": 137}, {"frame": 1, "bounds": {"left": -34, "right": 39, "top": -138, "bottom": 0}, "height": 138}, {"frame": 2, "bounds": {"left": -75, "right": 59, "top": -137, "bottom": 0}, "height": 137}, {"frame": 3, "bounds": {"left": -45, "right": 42, "top": -139, "bottom": 0}, "height": 139}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -28, "right": 66, "top": -150, "bottom": 0}, "height": 150}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -36, "right": 37, "top": -112, "bottom": 0}, "height": 112}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 49, "top": -151, "bottom": 0}, "height": 151}, {"frame": 1, "bounds": {"left": -45, "right": 47, "top": -125, "bottom": 0}, "height": 125}, {"frame": 2, "bounds": {"left": -50, "right": 57, "top": -135, "bottom": 0}, "height": 135}, {"frame": 3, "bounds": {"left": -48, "right": 48, "top": -149, "bottom": 0}, "height": 149}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -52, "right": 58, "top": -157, "bottom": 0}, "height": 157}, {"frame": 1, "bounds": {"left": -53, "right": 65, "top": -156, "bottom": 0}, "height": 156}, {"frame": 2, "bounds": {"left": -57, "right": 73, "top": -154, "bottom": 0}, "height": 154}, {"frame": 3, "bounds": {"left": -53, "right": 57, "top": -157, "bottom": 0}, "height": 157}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -58, "right": 57, "top": -157, "bottom": 0}, "height": 157}, {"frame": 1, "bounds": {"left": -59, "right": 51, "top": -157, "bottom": 0}, "height": 157}, {"frame": 2, "bounds": {"left": -60, "right": 73, "top": -158, "bottom": 0}, "height": 158}, {"frame": 3, "bounds": {"left": -55, "right": 52, "top": -157, "bottom": 0}, "height": 157}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 57, "top": -145, "bottom": 0}, "height": 145}, {"frame": 1, "bounds": {"left": -55, "right": 62, "top": -144, "bottom": 0}, "height": 144}, {"frame": 2, "bounds": {"left": -63, "right": 72, "top": -143, "bottom": 0}, "height": 143}, {"frame": 3, "bounds": {"left": -50, "right": 54, "top": -156, "bottom": 0}, "height": 156}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -52, "right": 56, "top": -128, "bottom": 0}, "height": 128}, {"frame": 1, "bounds": {"left": -53, "right": 56, "top": -119, "bottom": 0}, "height": 119}, {"frame": 2, "bounds": {"left": -60, "right": 62, "top": -110, "bottom": 0}, "height": 110}, {"frame": 3, "bounds": {"left": -51, "right": 51, "top": -144, "bottom": 0}, "height": 144}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -49, "right": 55, "top": -175, "bottom": 0}, "height": 175}, {"frame": 1, "bounds": {"left": -51, "right": 52, "top": -175, "bottom": 0}, "height": 175}, {"frame": 2, "bounds": {"left": -61, "right": 81, "top": -173, "bottom": 0}, "height": 173}, {"frame": 3, "bounds": {"left": -48, "right": 51, "top": -175, "bottom": 0}, "height": 175}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 56, "top": -159, "bottom": 0}, "height": 159}, {"frame": 1, "bounds": {"left": -50, "right": 51, "top": -186, "bottom": 0}, "height": 186}, {"frame": 2, "bounds": {"left": -52, "right": 63, "top": -149, "bottom": 0}, "height": 149}, {"frame": 3, "bounds": {"left": -56, "right": 65, "top": -159, "bottom": 0}, "height": 159}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -49, "right": 64, "top": -155, "bottom": 0}, "height": 155}, {"frame": 1, "bounds": {"left": -71, "right": 49, "top": -161, "bottom": 0}, "height": 161}, {"frame": 2, "bounds": {"left": -51, "right": 54, "top": -152, "bottom": 0}, "height": 152}, {"frame": 3, "bounds": {"left": -51, "right": 50, "top": -160, "bottom": 0}, "height": 160}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 54, "top": -111, "bottom": 0}, "height": 111}, {"frame": 1, "bounds": {"left": -69, "right": 50, "top": -76, "bottom": 0}, "height": 76}, {"frame": 2, "bounds": {"left": -51, "right": 68, "top": -49, "bottom": 0}, "height": 49}, {"frame": 3, "bounds": {"left": -53, "right": 69, "top": -28, "bottom": 0}, "height": 28}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -69, "right": 73, "top": -66, "bottom": 0}, "height": 66}, {"frame": 1, "bounds": {"left": -56, "right": 52, "top": -99, "bottom": 0}, "height": 99}, {"frame": 2, "bounds": {"left": -43, "right": 44, "top": -117, "bottom": 0}, "height": 117}, {"frame": 3, "bounds": {"left": -55, "right": 56, "top": -164, "bottom": 0}, "height": 164}]}}, "emitters": {"attack1": {"x": 73, "y": -123, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 73, "y": -122, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 72, "y": -76, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 62, "y": -17, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 81, "y": -135, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 63, "y": -76, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 19, "y": -75}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 0, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE KOLONI", "label": "KOLONI", "announce": "MODE KOLONI!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#c9a05a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["JAB KOLONI", "SEMUT GANDA", "SYUDUDU BERANTAI", "KOLONI RAKSASA"], "skills": {"skill1": {"kind": "homing", "damage": 3, "count": 10, "speed": 620, "turn": 5, "spread": 90, "size": 70, "knockback": 90, "duration": 0.6, "hitAt": 0.5, "asset": "ant", "life": 2.6}, "skill2": {"kind": "wave", "damage": 20, "speed": 700, "range": 600, "size": 320, "tall": 200, "knockback": 220, "duration": 0.62, "hitAt": 0.5, "asset": "soundchain", "pierce": true}}, "ultimate": {"kind": "stampede", "asset": "giantant", "count": 12, "gap": 0.18, "damage": 5, "speed": 1000, "size": 340, "lift": 70, "rx": 110, "ry": 120, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 30, "asset": "burst"}}, "cutin": {"a": "#150e02", "b": "#7a5a20", "kicker": "#ffe9b8", "title": "KOLONI
RAKSASA", "detail": "THE COLONY RISES"}}};
+window.FIGHTERS.gufron={"id": "gufron", "name": "GUFRON", "tag": "THE ANT WHISPERER", "race": "HUMAN", "title": "ANT WHISPERER", "color": "#c9a05a", "status": "ONE WITH THE ANTS", "select": "assets/menu/gufron-select.webp", "detail": "Quirky, calm, and followed by a very large colony.", "style": "Swarm / ants and sound", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["JAB NYENTRIK", "BAHASA SEMUT", "SYUDUDU", "KOLONI SEMUT RAYA"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "homing", "damage": 4, "count": 10, "speed": 520, "turn": 5, "spread": 60, "size": 70, "knockback": 90, "duration": 0.6, "hitAt": 0.5, "asset": "ant", "life": 2.4, "swarm": {"orbit": 0.55, "radius": 90, "stagger": 0.05}, "ramp": {"every": 5, "add": 1}}, "skill2": {"kind": "wave", "damage": 15, "speed": 640, "range": 520, "size": 220, "tall": 170, "knockback": 200, "duration": 0.62, "hitAt": 0.5, "asset": "soundwave", "pierce": true, "fx": "chain"}}, "ultimate": {"kind": "stampede", "asset": "antswarm", "count": 9, "gap": 0.24, "damage": 5, "speed": 900, "size": 280, "lift": 60, "rx": 100, "ry": 110, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 26, "asset": "burst"}}, "fx": ["ant", "antswarm", "burst", "giantant", "impact", "soundchain", "soundwave"], "cutin": {"a": "#1c1405", "b": "#5a4318", "kicker": "#ffe9b8", "title": "KOLONI
SEMUT RAYA", "detail": "THE COLONY ARRIVES"}, "manifest": {"characterId": "gufron", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 51, "top": -181, "bottom": 0}, "height": 181}, {"frame": 1, "bounds": {"left": -47, "right": 50, "top": -181, "bottom": 0}, "height": 181}, {"frame": 2, "bounds": {"left": -47, "right": 50, "top": -180, "bottom": 0}, "height": 180}, {"frame": 3, "bounds": {"left": -47, "right": 50, "top": -181, "bottom": 0}, "height": 181}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -45, "right": 47, "top": -156, "bottom": 0}, "height": 156}, {"frame": 1, "bounds": {"left": -26, "right": 47, "top": -156, "bottom": 0}, "height": 156}, {"frame": 2, "bounds": {"left": -45, "right": 48, "top": -156, "bottom": 0}, "height": 156}, {"frame": 3, "bounds": {"left": -33, "right": 42, "top": -156, "bottom": 0}, "height": 156}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -81, "right": 50, "top": -137, "bottom": 0}, "height": 137}, {"frame": 1, "bounds": {"left": -34, "right": 39, "top": -138, "bottom": 0}, "height": 138}, {"frame": 2, "bounds": {"left": -75, "right": 59, "top": -137, "bottom": 0}, "height": 137}, {"frame": 3, "bounds": {"left": -45, "right": 42, "top": -139, "bottom": 0}, "height": 139}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -28, "right": 66, "top": -150, "bottom": 0}, "height": 150}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -36, "right": 37, "top": -112, "bottom": 0}, "height": 112}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 49, "top": -151, "bottom": 0}, "height": 151}, {"frame": 1, "bounds": {"left": -45, "right": 47, "top": -125, "bottom": 0}, "height": 125}, {"frame": 2, "bounds": {"left": -50, "right": 57, "top": -135, "bottom": 0}, "height": 135}, {"frame": 3, "bounds": {"left": -48, "right": 48, "top": -149, "bottom": 0}, "height": 149}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -52, "right": 58, "top": -157, "bottom": 0}, "height": 157}, {"frame": 1, "bounds": {"left": -53, "right": 65, "top": -156, "bottom": 0}, "height": 156}, {"frame": 2, "bounds": {"left": -57, "right": 73, "top": -154, "bottom": 0}, "height": 154}, {"frame": 3, "bounds": {"left": -53, "right": 57, "top": -157, "bottom": 0}, "height": 157}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -58, "right": 57, "top": -157, "bottom": 0}, "height": 157}, {"frame": 1, "bounds": {"left": -59, "right": 51, "top": -157, "bottom": 0}, "height": 157}, {"frame": 2, "bounds": {"left": -60, "right": 73, "top": -158, "bottom": 0}, "height": 158}, {"frame": 3, "bounds": {"left": -55, "right": 52, "top": -157, "bottom": 0}, "height": 157}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 57, "top": -145, "bottom": 0}, "height": 145}, {"frame": 1, "bounds": {"left": -55, "right": 62, "top": -144, "bottom": 0}, "height": 144}, {"frame": 2, "bounds": {"left": -63, "right": 72, "top": -143, "bottom": 0}, "height": 143}, {"frame": 3, "bounds": {"left": -50, "right": 54, "top": -156, "bottom": 0}, "height": 156}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -52, "right": 56, "top": -128, "bottom": 0}, "height": 128}, {"frame": 1, "bounds": {"left": -53, "right": 56, "top": -119, "bottom": 0}, "height": 119}, {"frame": 2, "bounds": {"left": -60, "right": 62, "top": -110, "bottom": 0}, "height": 110}, {"frame": 3, "bounds": {"left": -51, "right": 51, "top": -144, "bottom": 0}, "height": 144}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -49, "right": 55, "top": -175, "bottom": 0}, "height": 175}, {"frame": 1, "bounds": {"left": -51, "right": 52, "top": -175, "bottom": 0}, "height": 175}, {"frame": 2, "bounds": {"left": -61, "right": 81, "top": -173, "bottom": 0}, "height": 173}, {"frame": 3, "bounds": {"left": -48, "right": 51, "top": -175, "bottom": 0}, "height": 175}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -55, "right": 56, "top": -159, "bottom": 0}, "height": 159}, {"frame": 1, "bounds": {"left": -50, "right": 51, "top": -186, "bottom": 0}, "height": 186}, {"frame": 2, "bounds": {"left": -52, "right": 63, "top": -149, "bottom": 0}, "height": 149}, {"frame": 3, "bounds": {"left": -56, "right": 65, "top": -159, "bottom": 0}, "height": 159}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -49, "right": 64, "top": -155, "bottom": 0}, "height": 155}, {"frame": 1, "bounds": {"left": -71, "right": 49, "top": -161, "bottom": 0}, "height": 161}, {"frame": 2, "bounds": {"left": -51, "right": 54, "top": -152, "bottom": 0}, "height": 152}, {"frame": 3, "bounds": {"left": -51, "right": 50, "top": -160, "bottom": 0}, "height": 160}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 54, "top": -111, "bottom": 0}, "height": 111}, {"frame": 1, "bounds": {"left": -69, "right": 50, "top": -76, "bottom": 0}, "height": 76}, {"frame": 2, "bounds": {"left": -51, "right": 68, "top": -49, "bottom": 0}, "height": 49}, {"frame": 3, "bounds": {"left": -53, "right": 69, "top": -28, "bottom": 0}, "height": 28}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -69, "right": 73, "top": -66, "bottom": 0}, "height": 66}, {"frame": 1, "bounds": {"left": -56, "right": 52, "top": -99, "bottom": 0}, "height": 99}, {"frame": 2, "bounds": {"left": -43, "right": 44, "top": -117, "bottom": 0}, "height": 117}, {"frame": 3, "bounds": {"left": -55, "right": 56, "top": -164, "bottom": 0}, "height": 164}]}}, "emitters": {"attack1": {"x": 73, "y": -123, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 73, "y": -122, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 72, "y": -76, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 62, "y": -17, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 81, "y": -135, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 63, "y": -76, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 19, "y": -75}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 0, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE KOLONI", "label": "KOLONI", "announce": "MODE KOLONI!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#c9a05a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["JAB KOLONI", "SEMUT GANDA", "SYUDUDU BERANTAI", "KOLONI RAKSASA"], "skills": {"skill1": {"kind": "homing", "damage": 3, "count": 11, "speed": 620, "turn": 5, "spread": 90, "size": 70, "knockback": 90, "duration": 0.6, "hitAt": 0.5, "asset": "ant", "life": 2.6, "swarm": {"orbit": 0.55, "radius": 100, "stagger": 0.045}}, "skill2": {"kind": "wave", "damage": 20, "speed": 700, "range": 600, "size": 320, "tall": 200, "knockback": 220, "duration": 0.62, "hitAt": 0.5, "asset": "soundchain", "pierce": true, "bind": 0.6}}, "ultimate": {"kind": "stampede", "asset": "giantant", "count": 12, "gap": 0.18, "damage": 5, "speed": 1000, "size": 340, "lift": 70, "rx": 110, "ry": 120, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 30, "asset": "burst"}}, "cutin": {"a": "#150e02", "b": "#7a5a20", "kicker": "#ffe9b8", "title": "KOLONI
RAKSASA", "detail": "THE COLONY RISES"}}};
diff --git a/assets/megachan/manifest.js b/assets/megachan/manifest.js
index 1807876..e151b81 100644
--- a/assets/megachan/manifest.js
+++ b/assets/megachan/manifest.js
@@ -1,2 +1,2 @@
window.FIGHTERS=window.FIGHTERS||{};
-window.FIGHTERS.megachan={"id": "megachan", "name": "MEGA CHAN", "tag": "THE RED BULL", "race": "HUMAN", "title": "RED BULL", "color": "#d62828", "status": "FIRED UP", "select": "assets/menu/megachan-select.webp", "detail": "A raised fist, a red blouse, and a herd on the way.", "style": "Matriarch / charge and sound wave", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["KEPALAN MERAH", "SRUDUK BANTENG", "ORASI LANTANG", "BANTENG HITAM"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "dash", "damage": 22, "dash": 720, "reach": 135, "knockback": 320, "duration": 0.6, "hitAt": 0.55, "asset": "bull", "size": 260}, "skill2": {"kind": "wave", "damage": 16, "speed": 650, "range": 540, "size": 230, "tall": 180, "knockback": 200, "duration": 0.62, "hitAt": 0.5, "asset": "soundwave", "pierce": true}}, "ultimate": {"kind": "stampede", "asset": "blackbull", "count": 8, "gap": 0.32, "damage": 6, "speed": 1050, "size": 320, "lift": 100, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["blackbull", "bull", "bullflame", "burst", "firewave", "hellbull", "impact", "soundwave"], "cutin": {"a": "#1a0505", "b": "#7a1414", "kicker": "#ffd0c0", "title": "BANTENG
HITAM", "detail": "STAMPEDE OF BLACK BULLS"}, "manifest": {"characterId": "megachan", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "green", "hex": "#00FF00", "rgb": [0, 255, 0], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -61, "right": 62, "top": -181, "bottom": 0}, "height": 181}, {"frame": 1, "bounds": {"left": -60, "right": 60, "top": -181, "bottom": 0}, "height": 181}, {"frame": 2, "bounds": {"left": -62, "right": 62, "top": -172, "bottom": 0}, "height": 172}, {"frame": 3, "bounds": {"left": -61, "right": 60, "top": -181, "bottom": 0}, "height": 181}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 59, "top": -196, "bottom": 0}, "height": 196}, {"frame": 1, "bounds": {"left": -39, "right": 57, "top": -199, "bottom": 0}, "height": 199}, {"frame": 2, "bounds": {"left": -58, "right": 60, "top": -197, "bottom": 0}, "height": 197}, {"frame": 3, "bounds": {"left": -55, "right": 54, "top": -196, "bottom": 0}, "height": 196}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 60, "top": -172, "bottom": 0}, "height": 172}, {"frame": 1, "bounds": {"left": -26, "right": 91, "top": -173, "bottom": 0}, "height": 173}, {"frame": 2, "bounds": {"left": -94, "right": 48, "top": -172, "bottom": 0}, "height": 172}, {"frame": 3, "bounds": {"left": -88, "right": 35, "top": -172, "bottom": 0}, "height": 172}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -21, "right": 86, "top": -173, "bottom": 0}, "height": 173}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -34, "right": 37, "top": -123, "bottom": 0}, "height": 123}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -53, "right": 71, "top": -166, "bottom": 0}, "height": 166}, {"frame": 1, "bounds": {"left": -54, "right": 60, "top": -149, "bottom": 0}, "height": 149}, {"frame": 2, "bounds": {"left": -58, "right": 81, "top": -151, "bottom": 0}, "height": 151}, {"frame": 3, "bounds": {"left": -55, "right": 59, "top": -163, "bottom": 0}, "height": 163}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 62, "top": -170, "bottom": 0}, "height": 170}, {"frame": 1, "bounds": {"left": -59, "right": 72, "top": -170, "bottom": 0}, "height": 170}, {"frame": 2, "bounds": {"left": -67, "right": 85, "top": -170, "bottom": 0}, "height": 170}, {"frame": 3, "bounds": {"left": -61, "right": 62, "top": -170, "bottom": 0}, "height": 170}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 67, "top": -173, "bottom": 0}, "height": 173}, {"frame": 1, "bounds": {"left": -60, "right": 65, "top": -175, "bottom": 0}, "height": 175}, {"frame": 2, "bounds": {"left": -63, "right": 97, "top": -176, "bottom": 0}, "height": 176}, {"frame": 3, "bounds": {"left": -60, "right": 63, "top": -174, "bottom": 0}, "height": 174}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 63, "top": -171, "bottom": 0}, "height": 171}, {"frame": 1, "bounds": {"left": -70, "right": 63, "top": -172, "bottom": 0}, "height": 172}, {"frame": 2, "bounds": {"left": -78, "right": 97, "top": -166, "bottom": 0}, "height": 166}, {"frame": 3, "bounds": {"left": -62, "right": 66, "top": -173, "bottom": 0}, "height": 173}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -61, "right": 63, "top": -134, "bottom": 0}, "height": 134}, {"frame": 1, "bounds": {"left": -64, "right": 81, "top": -137, "bottom": 0}, "height": 137}, {"frame": 2, "bounds": {"left": -38, "right": 136, "top": -152, "bottom": 0}, "height": 152}, {"frame": 3, "bounds": {"left": -68, "right": 72, "top": -153, "bottom": 0}, "height": 153}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -62, "right": 64, "top": -190, "bottom": 0}, "height": 190}, {"frame": 1, "bounds": {"left": -62, "right": 66, "top": -192, "bottom": 0}, "height": 192}, {"frame": 2, "bounds": {"left": -72, "right": 78, "top": -189, "bottom": 0}, "height": 189}, {"frame": 3, "bounds": {"left": -60, "right": 63, "top": -190, "bottom": 0}, "height": 190}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -71, "right": 69, "top": -159, "bottom": 0}, "height": 159}, {"frame": 1, "bounds": {"left": -65, "right": 66, "top": -196, "bottom": 0}, "height": 196}, {"frame": 2, "bounds": {"left": -67, "right": 75, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -67, "right": 75, "top": -174, "bottom": 0}, "height": 174}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -84, "right": 61, "top": -185, "bottom": 0}, "height": 185}, {"frame": 1, "bounds": {"left": -117, "right": 69, "top": -182, "bottom": 0}, "height": 182}, {"frame": 2, "bounds": {"left": -54, "right": 61, "top": -179, "bottom": 0}, "height": 179}, {"frame": 3, "bounds": {"left": -66, "right": 68, "top": -185, "bottom": 0}, "height": 185}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 70, "top": -144, "bottom": 0}, "height": 144}, {"frame": 1, "bounds": {"left": -109, "right": 29, "top": -119, "bottom": 0}, "height": 119}, {"frame": 2, "bounds": {"left": -82, "right": 64, "top": -83, "bottom": 0}, "height": 83}, {"frame": 3, "bounds": {"left": -67, "right": 91, "top": -55, "bottom": 0}, "height": 55}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -85, "right": 72, "top": -90, "bottom": 0}, "height": 90}, {"frame": 1, "bounds": {"left": -66, "right": 55, "top": -121, "bottom": 0}, "height": 121}, {"frame": 2, "bounds": {"left": -42, "right": 53, "top": -144, "bottom": 0}, "height": 144}, {"frame": 3, "bounds": {"left": -61, "right": 65, "top": -161, "bottom": 0}, "height": 161}]}}, "emitters": {"attack1": {"x": 85, "y": -108, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 97, "y": -118, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 97, "y": -102, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 136, "y": -87, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 78, "y": -132, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 75, "y": -121, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 32, "y": -87}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 1, "y": -62}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "BANTENG MURKA", "label": "MURKA", "announce": "BANTENG MURKA!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#ff2a2a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["TINJU MURKA", "SERUDUK GANDA", "ORASI MEMBARA", "TANDUK NERAKA"], "skills": {"skill1": {"kind": "dash", "damage": 26, "dash": 920, "reach": 150, "knockback": 360, "duration": 0.6, "hitAt": 0.55, "asset": "bullflame", "size": 300}, "skill2": {"kind": "wave", "damage": 22, "speed": 700, "range": 600, "size": 320, "tall": 200, "knockback": 240, "duration": 0.62, "hitAt": 0.5, "asset": "firewave", "pierce": true}}, "ultimate": {"kind": "stampede", "asset": "hellbull", "count": 10, "gap": 0.26, "damage": 7, "speed": 1150, "size": 380, "lift": 110, "rx": 110, "ry": 140, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 32, "asset": "burst"}}, "cutin": {"a": "#1a0000", "b": "#8a1010", "kicker": "#ffc8b8", "title": "TANDUK
NERAKA", "detail": "THE HERD BURNS"}}};
+window.FIGHTERS.megachan={"id": "megachan", "name": "MEGA CHAN", "tag": "THE RED BULL", "race": "HUMAN", "title": "RED BULL", "color": "#d62828", "status": "FIRED UP", "select": "assets/menu/megachan-select.webp", "detail": "A raised fist, a red blouse, and a herd on the way.", "style": "Matriarch / charge and sound wave", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["KEPALAN MERAH", "SRUDUK BANTENG", "ORASI LANTANG", "BANTENG HITAM"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "dash", "damage": 22, "dash": 720, "reach": 135, "knockback": 320, "duration": 0.6, "hitAt": 0.55, "asset": "bull", "size": 260}, "skill2": {"kind": "wave", "damage": 16, "speed": 650, "range": 540, "size": 230, "tall": 180, "knockback": 200, "duration": 0.62, "hitAt": 0.5, "asset": "soundwave", "pierce": true, "fx": "rings"}}, "ultimate": {"kind": "stampede", "asset": "blackbull", "count": 8, "gap": 0.32, "damage": 6, "speed": 1050, "size": 320, "lift": 100, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["blackbull", "bull", "bullflame", "burst", "firewave", "hellbull", "impact", "soundwave"], "cutin": {"a": "#1a0505", "b": "#7a1414", "kicker": "#ffd0c0", "title": "BANTENG
HITAM", "detail": "STAMPEDE OF BLACK BULLS"}, "manifest": {"characterId": "megachan", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "green", "hex": "#00FF00", "rgb": [0, 255, 0], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -61, "right": 62, "top": -181, "bottom": 0}, "height": 181}, {"frame": 1, "bounds": {"left": -60, "right": 60, "top": -181, "bottom": 0}, "height": 181}, {"frame": 2, "bounds": {"left": -62, "right": 62, "top": -172, "bottom": 0}, "height": 172}, {"frame": 3, "bounds": {"left": -61, "right": 60, "top": -181, "bottom": 0}, "height": 181}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 59, "top": -196, "bottom": 0}, "height": 196}, {"frame": 1, "bounds": {"left": -39, "right": 57, "top": -199, "bottom": 0}, "height": 199}, {"frame": 2, "bounds": {"left": -58, "right": 60, "top": -197, "bottom": 0}, "height": 197}, {"frame": 3, "bounds": {"left": -55, "right": 54, "top": -196, "bottom": 0}, "height": 196}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 60, "top": -172, "bottom": 0}, "height": 172}, {"frame": 1, "bounds": {"left": -26, "right": 91, "top": -173, "bottom": 0}, "height": 173}, {"frame": 2, "bounds": {"left": -94, "right": 48, "top": -172, "bottom": 0}, "height": 172}, {"frame": 3, "bounds": {"left": -88, "right": 35, "top": -172, "bottom": 0}, "height": 172}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -21, "right": 86, "top": -173, "bottom": 0}, "height": 173}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -34, "right": 37, "top": -123, "bottom": 0}, "height": 123}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -53, "right": 71, "top": -166, "bottom": 0}, "height": 166}, {"frame": 1, "bounds": {"left": -54, "right": 60, "top": -149, "bottom": 0}, "height": 149}, {"frame": 2, "bounds": {"left": -58, "right": 81, "top": -151, "bottom": 0}, "height": 151}, {"frame": 3, "bounds": {"left": -55, "right": 59, "top": -163, "bottom": 0}, "height": 163}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 62, "top": -170, "bottom": 0}, "height": 170}, {"frame": 1, "bounds": {"left": -59, "right": 72, "top": -170, "bottom": 0}, "height": 170}, {"frame": 2, "bounds": {"left": -67, "right": 85, "top": -170, "bottom": 0}, "height": 170}, {"frame": 3, "bounds": {"left": -61, "right": 62, "top": -170, "bottom": 0}, "height": 170}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 67, "top": -173, "bottom": 0}, "height": 173}, {"frame": 1, "bounds": {"left": -60, "right": 65, "top": -175, "bottom": 0}, "height": 175}, {"frame": 2, "bounds": {"left": -63, "right": 97, "top": -176, "bottom": 0}, "height": 176}, {"frame": 3, "bounds": {"left": -60, "right": 63, "top": -174, "bottom": 0}, "height": 174}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 63, "top": -171, "bottom": 0}, "height": 171}, {"frame": 1, "bounds": {"left": -70, "right": 63, "top": -172, "bottom": 0}, "height": 172}, {"frame": 2, "bounds": {"left": -78, "right": 97, "top": -166, "bottom": 0}, "height": 166}, {"frame": 3, "bounds": {"left": -62, "right": 66, "top": -173, "bottom": 0}, "height": 173}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -61, "right": 63, "top": -134, "bottom": 0}, "height": 134}, {"frame": 1, "bounds": {"left": -64, "right": 81, "top": -137, "bottom": 0}, "height": 137}, {"frame": 2, "bounds": {"left": -38, "right": 136, "top": -152, "bottom": 0}, "height": 152}, {"frame": 3, "bounds": {"left": -68, "right": 72, "top": -153, "bottom": 0}, "height": 153}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -62, "right": 64, "top": -190, "bottom": 0}, "height": 190}, {"frame": 1, "bounds": {"left": -62, "right": 66, "top": -192, "bottom": 0}, "height": 192}, {"frame": 2, "bounds": {"left": -72, "right": 78, "top": -189, "bottom": 0}, "height": 189}, {"frame": 3, "bounds": {"left": -60, "right": 63, "top": -190, "bottom": 0}, "height": 190}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -71, "right": 69, "top": -159, "bottom": 0}, "height": 159}, {"frame": 1, "bounds": {"left": -65, "right": 66, "top": -196, "bottom": 0}, "height": 196}, {"frame": 2, "bounds": {"left": -67, "right": 75, "top": -178, "bottom": 0}, "height": 178}, {"frame": 3, "bounds": {"left": -67, "right": 75, "top": -174, "bottom": 0}, "height": 174}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -84, "right": 61, "top": -185, "bottom": 0}, "height": 185}, {"frame": 1, "bounds": {"left": -117, "right": 69, "top": -182, "bottom": 0}, "height": 182}, {"frame": 2, "bounds": {"left": -54, "right": 61, "top": -179, "bottom": 0}, "height": 179}, {"frame": 3, "bounds": {"left": -66, "right": 68, "top": -185, "bottom": 0}, "height": 185}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 70, "top": -144, "bottom": 0}, "height": 144}, {"frame": 1, "bounds": {"left": -109, "right": 29, "top": -119, "bottom": 0}, "height": 119}, {"frame": 2, "bounds": {"left": -82, "right": 64, "top": -83, "bottom": 0}, "height": 83}, {"frame": 3, "bounds": {"left": -67, "right": 91, "top": -55, "bottom": 0}, "height": 55}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -85, "right": 72, "top": -90, "bottom": 0}, "height": 90}, {"frame": 1, "bounds": {"left": -66, "right": 55, "top": -121, "bottom": 0}, "height": 121}, {"frame": 2, "bounds": {"left": -42, "right": 53, "top": -144, "bottom": 0}, "height": 144}, {"frame": 3, "bounds": {"left": -61, "right": 65, "top": -161, "bottom": 0}, "height": 161}]}}, "emitters": {"attack1": {"x": 85, "y": -108, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 97, "y": -118, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 97, "y": -102, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 136, "y": -87, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 78, "y": -132, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 75, "y": -121, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 32, "y": -87}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 1, "y": -62}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "BANTENG MURKA", "label": "MURKA", "announce": "BANTENG MURKA!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#ff2a2a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["TINJU MURKA", "SERUDUK GANDA", "ORASI MEMBARA", "TANDUK NERAKA"], "skills": {"skill1": {"kind": "dash", "damage": 16, "dash": 920, "reach": 150, "knockback": 360, "duration": 0.6, "hitAt": 0.55, "asset": "bullflame", "size": 300, "rebound": {"range": 140, "damage": 12, "speed": 1100, "asset": "bullflame", "size": 300}}, "skill2": {"kind": "wave", "damage": 20, "speed": 700, "range": 600, "size": 320, "tall": 200, "knockback": 240, "duration": 0.62, "hitAt": 0.5, "asset": "firewave", "pierce": true, "fx": "flame", "trail": {"gap": 115, "radius": 60, "dps": 3, "life": 3}}}, "ultimate": {"kind": "stampede", "asset": "hellbull", "count": 10, "gap": 0.26, "damage": 7, "speed": 1150, "size": 380, "lift": 110, "rx": 110, "ry": 140, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 32, "asset": "burst"}}, "cutin": {"a": "#1a0000", "b": "#8a1010", "kicker": "#ffc8b8", "title": "TANDUK
NERAKA", "detail": "THE HERD BURNS"}}};
diff --git a/assets/soloman/manifest.js b/assets/soloman/manifest.js
index 70d0502..219b4a2 100644
--- a/assets/soloman/manifest.js
+++ b/assets/soloman/manifest.js
@@ -1,2 +1,2 @@
window.FIGHTERS=window.FIGHTERS||{};
-window.FIGHTERS.soloman={"id": "soloman", "name": "SOLO MAN", "tag": "THE SIMPLE ONE", "race": "HUMAN", "title": "SIMPLE ONE", "color": "#e0b04a", "status": "ON THE ROUTE", "select": "assets/menu/soloman-select.webp", "detail": "Soft-spoken, slim, and then a convoy arrives.", "style": "Rushdown / dash and cloth", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["KIBAS SARUNG", "ESEMKA RUSH", "SARUNG PUTAR", "KONVOI ESEMKA"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "dash", "damage": 20, "dash": 760, "reach": 140, "knockback": 300, "duration": 0.6, "hitAt": 0.55, "asset": "car", "size": 260}, "skill2": {"kind": "area", "damage": 16, "ahead": 0, "radius": 140, "vy": 140, "knockback": 280, "duration": 0.62, "hitAt": 0.5, "asset": "sarong"}}, "ultimate": {"kind": "stampede", "asset": "car", "count": 6, "gap": 0.4, "damage": 8, "speed": 1100, "size": 360, "lift": 100, "rx": 110, "ry": 140, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["bigcar", "burst", "car", "impact", "sarong", "sarongstorm"], "cutin": {"a": "#2a1c05", "b": "#8a6416", "kicker": "#fff0c0", "title": "KONVOI
ESEMKA", "detail": "A LINE OF CARS"}, "manifest": {"characterId": "soloman", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "green", "hex": "#00FF00", "rgb": [0, 255, 0], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -49, "right": 52, "top": -180, "bottom": 0}, "height": 180}, {"frame": 1, "bounds": {"left": -50, "right": 52, "top": -179, "bottom": 0}, "height": 179}, {"frame": 2, "bounds": {"left": -49, "right": 52, "top": -181, "bottom": 0}, "height": 181}, {"frame": 3, "bounds": {"left": -50, "right": 52, "top": -180, "bottom": 0}, "height": 180}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 55, "top": -166, "bottom": 0}, "height": 166}, {"frame": 1, "bounds": {"left": -24, "right": 34, "top": -168, "bottom": 0}, "height": 168}, {"frame": 2, "bounds": {"left": -50, "right": 53, "top": -166, "bottom": 0}, "height": 166}, {"frame": 3, "bounds": {"left": -41, "right": 48, "top": -166, "bottom": 0}, "height": 166}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -74, "right": 48, "top": -144, "bottom": 0}, "height": 144}, {"frame": 1, "bounds": {"left": -35, "right": 42, "top": -147, "bottom": 0}, "height": 147}, {"frame": 2, "bounds": {"left": -90, "right": 46, "top": -145, "bottom": 0}, "height": 145}, {"frame": 3, "bounds": {"left": -43, "right": 49, "top": -146, "bottom": 0}, "height": 146}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -33, "right": 68, "top": -151, "bottom": 0}, "height": 151}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -25, "right": 39, "top": -112, "bottom": 0}, "height": 112}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 57, "top": -150, "bottom": 0}, "height": 150}, {"frame": 1, "bounds": {"left": -49, "right": 53, "top": -123, "bottom": 0}, "height": 123}, {"frame": 2, "bounds": {"left": -56, "right": 61, "top": -136, "bottom": 0}, "height": 136}, {"frame": 3, "bounds": {"left": -48, "right": 53, "top": -152, "bottom": 0}, "height": 152}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -45, "right": 48, "top": -148, "bottom": 0}, "height": 148}, {"frame": 1, "bounds": {"left": -50, "right": 71, "top": -147, "bottom": 0}, "height": 147}, {"frame": 2, "bounds": {"left": -54, "right": 85, "top": -147, "bottom": 0}, "height": 147}, {"frame": 3, "bounds": {"left": -45, "right": 49, "top": -148, "bottom": 0}, "height": 148}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 57, "top": -168, "bottom": 0}, "height": 168}, {"frame": 1, "bounds": {"left": -59, "right": 55, "top": -168, "bottom": 0}, "height": 168}, {"frame": 2, "bounds": {"left": -61, "right": 65, "top": -167, "bottom": 0}, "height": 167}, {"frame": 3, "bounds": {"left": -55, "right": 57, "top": -168, "bottom": 0}, "height": 168}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -59, "right": 59, "top": -138, "bottom": 0}, "height": 138}, {"frame": 1, "bounds": {"left": -55, "right": 46, "top": -136, "bottom": 0}, "height": 136}, {"frame": 2, "bounds": {"left": -64, "right": 88, "top": -132, "bottom": 0}, "height": 132}, {"frame": 3, "bounds": {"left": -49, "right": 50, "top": -150, "bottom": 0}, "height": 150}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 48, "top": -98, "bottom": 0}, "height": 98}, {"frame": 1, "bounds": {"left": -53, "right": 66, "top": -120, "bottom": 0}, "height": 120}, {"frame": 2, "bounds": {"left": -36, "right": 118, "top": -108, "bottom": 0}, "height": 108}, {"frame": 3, "bounds": {"left": -45, "right": 45, "top": -137, "bottom": 0}, "height": 137}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 51, "top": -141, "bottom": 0}, "height": 141}, {"frame": 1, "bounds": {"left": -57, "right": 62, "top": -135, "bottom": 0}, "height": 135}, {"frame": 2, "bounds": {"left": -54, "right": 68, "top": -145, "bottom": 0}, "height": 145}, {"frame": 3, "bounds": {"left": -52, "right": 52, "top": -139, "bottom": 0}, "height": 139}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 51, "top": -125, "bottom": 0}, "height": 125}, {"frame": 1, "bounds": {"left": -73, "right": 69, "top": -151, "bottom": 0}, "height": 151}, {"frame": 2, "bounds": {"left": -51, "right": 69, "top": -142, "bottom": 0}, "height": 142}, {"frame": 3, "bounds": {"left": -59, "right": 72, "top": -137, "bottom": 0}, "height": 137}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 54, "top": -150, "bottom": 0}, "height": 150}, {"frame": 1, "bounds": {"left": -64, "right": 54, "top": -155, "bottom": 0}, "height": 155}, {"frame": 2, "bounds": {"left": -48, "right": 53, "top": -150, "bottom": 0}, "height": 150}, {"frame": 3, "bounds": {"left": -49, "right": 51, "top": -157, "bottom": 0}, "height": 157}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 46, "top": -112, "bottom": 0}, "height": 112}, {"frame": 1, "bounds": {"left": -90, "right": 21, "top": -96, "bottom": 0}, "height": 96}, {"frame": 2, "bounds": {"left": -56, "right": 61, "top": -62, "bottom": 0}, "height": 62}, {"frame": 3, "bounds": {"left": -63, "right": 64, "top": -31, "bottom": 0}, "height": 31}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -64, "right": 67, "top": -52, "bottom": 0}, "height": 52}, {"frame": 1, "bounds": {"left": -67, "right": 52, "top": -81, "bottom": 0}, "height": 81}, {"frame": 2, "bounds": {"left": -35, "right": 41, "top": -108, "bottom": 0}, "height": 108}, {"frame": 3, "bounds": {"left": -45, "right": 47, "top": -145, "bottom": 0}, "height": 145}]}}, "emitters": {"attack1": {"x": 85, "y": -111, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 65, "y": -116, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 88, "y": -108, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 118, "y": -69, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 68, "y": -89, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 69, "y": -112, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 17, "y": -76}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 7, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE BLUSUKAN", "label": "BLUSUKAN", "announce": "MODE BLUSUKAN!", "duration": 10, "damageMul": 1.35, "speedMul": 1.25, "reachAdd": 20, "aura": "#ffd24a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["KIBAS BADAI", "KONVOI MINI", "SARUNG BADAI", "KONVOI RAKSASA"], "skills": {"skill1": {"kind": "wave", "damage": 22, "speed": 900, "range": 640, "size": 330, "tall": 180, "knockback": 260, "duration": 0.6, "hitAt": 0.5, "asset": "car", "pierce": true}, "skill2": {"kind": "area", "damage": 26, "ahead": 0, "radius": 200, "vy": 150, "knockback": 320, "duration": 0.62, "hitAt": 0.5, "asset": "sarongstorm"}}, "ultimate": {"kind": "stampede", "asset": "bigcar", "count": 9, "gap": 0.28, "damage": 6, "speed": 1200, "size": 430, "lift": 120, "rx": 120, "ry": 150, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "cutin": {"a": "#2a1c00", "b": "#b8861a", "kicker": "#fff2c8", "title": "KONVOI
RAKSASA", "detail": "THE ROUTE NEVER ENDS"}}};
+window.FIGHTERS.soloman={"id": "soloman", "name": "SOLO MAN", "tag": "THE SIMPLE ONE", "race": "HUMAN", "title": "SIMPLE ONE", "color": "#e0b04a", "status": "ON THE ROUTE", "select": "assets/menu/soloman-select.webp", "detail": "Soft-spoken, slim, and then a convoy arrives.", "style": "Rushdown / dash and cloth", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["KIBAS SARUNG", "ESEMKA RUSH", "SARUNG PUTAR", "KONVOI ESEMKA"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "dash", "damage": 20, "dash": 760, "reach": 140, "knockback": 300, "duration": 0.6, "hitAt": 0.55, "asset": "car", "size": 260}, "skill2": {"kind": "area", "damage": 16, "ahead": 0, "radius": 140, "vy": 140, "knockback": 280, "duration": 0.62, "hitAt": 0.5, "asset": "sarong"}}, "ultimate": {"kind": "stampede", "asset": "car", "count": 6, "gap": 0.4, "damage": 8, "speed": 1100, "size": 360, "lift": 100, "rx": 110, "ry": 140, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["bigcar", "burst", "car", "impact", "sarong", "sarongstorm"], "cutin": {"a": "#2a1c05", "b": "#8a6416", "kicker": "#fff0c0", "title": "KONVOI
ESEMKA", "detail": "A LINE OF CARS"}, "manifest": {"characterId": "soloman", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "green", "hex": "#00FF00", "rgb": [0, 255, 0], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -49, "right": 52, "top": -180, "bottom": 0}, "height": 180}, {"frame": 1, "bounds": {"left": -50, "right": 52, "top": -179, "bottom": 0}, "height": 179}, {"frame": 2, "bounds": {"left": -49, "right": 52, "top": -181, "bottom": 0}, "height": 181}, {"frame": 3, "bounds": {"left": -50, "right": 52, "top": -180, "bottom": 0}, "height": 180}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 55, "top": -166, "bottom": 0}, "height": 166}, {"frame": 1, "bounds": {"left": -24, "right": 34, "top": -168, "bottom": 0}, "height": 168}, {"frame": 2, "bounds": {"left": -50, "right": 53, "top": -166, "bottom": 0}, "height": 166}, {"frame": 3, "bounds": {"left": -41, "right": 48, "top": -166, "bottom": 0}, "height": 166}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -74, "right": 48, "top": -144, "bottom": 0}, "height": 144}, {"frame": 1, "bounds": {"left": -35, "right": 42, "top": -147, "bottom": 0}, "height": 147}, {"frame": 2, "bounds": {"left": -90, "right": 46, "top": -145, "bottom": 0}, "height": 145}, {"frame": 3, "bounds": {"left": -43, "right": 49, "top": -146, "bottom": 0}, "height": 146}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -33, "right": 68, "top": -151, "bottom": 0}, "height": 151}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -25, "right": 39, "top": -112, "bottom": 0}, "height": 112}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 57, "top": -150, "bottom": 0}, "height": 150}, {"frame": 1, "bounds": {"left": -49, "right": 53, "top": -123, "bottom": 0}, "height": 123}, {"frame": 2, "bounds": {"left": -56, "right": 61, "top": -136, "bottom": 0}, "height": 136}, {"frame": 3, "bounds": {"left": -48, "right": 53, "top": -152, "bottom": 0}, "height": 152}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -45, "right": 48, "top": -148, "bottom": 0}, "height": 148}, {"frame": 1, "bounds": {"left": -50, "right": 71, "top": -147, "bottom": 0}, "height": 147}, {"frame": 2, "bounds": {"left": -54, "right": 85, "top": -147, "bottom": 0}, "height": 147}, {"frame": 3, "bounds": {"left": -45, "right": 49, "top": -148, "bottom": 0}, "height": 148}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -60, "right": 57, "top": -168, "bottom": 0}, "height": 168}, {"frame": 1, "bounds": {"left": -59, "right": 55, "top": -168, "bottom": 0}, "height": 168}, {"frame": 2, "bounds": {"left": -61, "right": 65, "top": -167, "bottom": 0}, "height": 167}, {"frame": 3, "bounds": {"left": -55, "right": 57, "top": -168, "bottom": 0}, "height": 168}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -59, "right": 59, "top": -138, "bottom": 0}, "height": 138}, {"frame": 1, "bounds": {"left": -55, "right": 46, "top": -136, "bottom": 0}, "height": 136}, {"frame": 2, "bounds": {"left": -64, "right": 88, "top": -132, "bottom": 0}, "height": 132}, {"frame": 3, "bounds": {"left": -49, "right": 50, "top": -150, "bottom": 0}, "height": 150}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 48, "top": -98, "bottom": 0}, "height": 98}, {"frame": 1, "bounds": {"left": -53, "right": 66, "top": -120, "bottom": 0}, "height": 120}, {"frame": 2, "bounds": {"left": -36, "right": 118, "top": -108, "bottom": 0}, "height": 108}, {"frame": 3, "bounds": {"left": -45, "right": 45, "top": -137, "bottom": 0}, "height": 137}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -54, "right": 51, "top": -141, "bottom": 0}, "height": 141}, {"frame": 1, "bounds": {"left": -57, "right": 62, "top": -135, "bottom": 0}, "height": 135}, {"frame": 2, "bounds": {"left": -54, "right": 68, "top": -145, "bottom": 0}, "height": 145}, {"frame": 3, "bounds": {"left": -52, "right": 52, "top": -139, "bottom": 0}, "height": 139}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -50, "right": 51, "top": -125, "bottom": 0}, "height": 125}, {"frame": 1, "bounds": {"left": -73, "right": 69, "top": -151, "bottom": 0}, "height": 151}, {"frame": 2, "bounds": {"left": -51, "right": 69, "top": -142, "bottom": 0}, "height": 142}, {"frame": 3, "bounds": {"left": -59, "right": 72, "top": -137, "bottom": 0}, "height": 137}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 54, "top": -150, "bottom": 0}, "height": 150}, {"frame": 1, "bounds": {"left": -64, "right": 54, "top": -155, "bottom": 0}, "height": 155}, {"frame": 2, "bounds": {"left": -48, "right": 53, "top": -150, "bottom": 0}, "height": 150}, {"frame": 3, "bounds": {"left": -49, "right": 51, "top": -157, "bottom": 0}, "height": 157}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -63, "right": 46, "top": -112, "bottom": 0}, "height": 112}, {"frame": 1, "bounds": {"left": -90, "right": 21, "top": -96, "bottom": 0}, "height": 96}, {"frame": 2, "bounds": {"left": -56, "right": 61, "top": -62, "bottom": 0}, "height": 62}, {"frame": 3, "bounds": {"left": -63, "right": 64, "top": -31, "bottom": 0}, "height": 31}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -64, "right": 67, "top": -52, "bottom": 0}, "height": 52}, {"frame": 1, "bounds": {"left": -67, "right": 52, "top": -81, "bottom": 0}, "height": 81}, {"frame": 2, "bounds": {"left": -35, "right": 41, "top": -108, "bottom": 0}, "height": 108}, {"frame": 3, "bounds": {"left": -45, "right": 47, "top": -145, "bottom": 0}, "height": 145}]}}, "emitters": {"attack1": {"x": 85, "y": -111, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 65, "y": -116, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 88, "y": -108, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 118, "y": -69, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 68, "y": -89, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 69, "y": -112, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 17, "y": -76}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 7, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE BLUSUKAN", "label": "BLUSUKAN", "announce": "MODE BLUSUKAN!", "duration": 10, "damageMul": 1.35, "speedMul": 1.25, "reachAdd": 20, "aura": "#ffd24a", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["KIBAS BADAI", "KONVOI MINI", "SARUNG BADAI", "KONVOI RAKSASA"], "skills": {"skill1": {"kind": "wave", "damage": 22, "speed": 900, "range": 640, "size": 330, "tall": 180, "knockback": 260, "duration": 0.6, "hitAt": 0.5, "asset": "car", "pierce": true, "fx": "drive", "convoy": {"n": 3, "mul": 0.36, "gap": 0.14}}, "skill2": {"kind": "area", "damage": 26, "ahead": 0, "radius": 200, "vy": 150, "knockback": 320, "duration": 0.62, "hitAt": 0.5, "asset": "sarongstorm", "pull": 520}}, "ultimate": {"kind": "stampede", "asset": "bigcar", "count": 9, "gap": 0.28, "damage": 6, "speed": 1200, "size": 430, "lift": 120, "rx": 120, "ry": 150, "hold": 1.2, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "cutin": {"a": "#2a1c00", "b": "#b8861a", "kicker": "#fff2c8", "title": "KONVOI
RAKSASA", "detail": "THE ROUTE NEVER ENDS"}}};
diff --git a/assets/sonofsolo/manifest.js b/assets/sonofsolo/manifest.js
index 4e0cf91..dcae0d2 100644
--- a/assets/sonofsolo/manifest.js
+++ b/assets/sonofsolo/manifest.js
@@ -1,2 +1,2 @@
window.FIGHTERS=window.FIGHTERS||{};
-window.FIGHTERS.sonofsolo={"id": "sonofsolo", "name": "SON OF SOLO MAN", "tag": "THE ACID HEIR", "race": "HUMAN", "title": "ACID HEIR", "color": "#7bd23c", "status": "COOL HEADED", "select": "assets/menu/sonofsolo-select.webp", "detail": "A calm stare, a flask, and an acid storm.", "style": "Zoner / acid flask and shouts", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["JAB CEPAT", "ASAM SULFAT", "TERIAKAN", "HUJAN ASAM"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "lob", "damage": 14, "rise": 700, "gravity": 1800, "radius": 115, "dps": 6, "puddleLife": 3.5, "puddle": "puddle", "asset": "flask", "size": 90, "knockback": 160, "duration": 0.6, "hitAt": 0.55}, "skill2": {"kind": "projectile", "damage": 15, "speed": 820, "range": 560, "size": 170, "knockback": 200, "duration": 0.6, "hitAt": 0.5, "asset": "soundwave"}}, "ultimate": {"kind": "rain", "asset": "drop", "count": 14, "gap": 0.12, "damage": 3, "radius": 105, "spread": 380, "size": 150, "delay": 0.4, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["acidspray", "bigpuddle", "burst", "drop", "flask", "impact", "puddle", "soundwave"], "cutin": {"a": "#0b1d0b", "b": "#2f6b1c", "kicker": "#d6ffb0", "title": "HUJAN
ASAM", "detail": "ACID DOWNPOUR"}, "manifest": {"characterId": "sonofsolo", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -33, "right": 38, "top": -180, "bottom": 0}, "height": 180}, {"frame": 1, "bounds": {"left": -33, "right": 38, "top": -183, "bottom": 0}, "height": 183}, {"frame": 2, "bounds": {"left": -33, "right": 38, "top": -180, "bottom": 0}, "height": 180}, {"frame": 3, "bounds": {"left": -32, "right": 39, "top": -180, "bottom": 0}, "height": 180}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -42, "right": 45, "top": -167, "bottom": 0}, "height": 167}, {"frame": 1, "bounds": {"left": -27, "right": 37, "top": -170, "bottom": 0}, "height": 170}, {"frame": 2, "bounds": {"left": -42, "right": 45, "top": -168, "bottom": 0}, "height": 168}, {"frame": 3, "bounds": {"left": -26, "right": 45, "top": -168, "bottom": 0}, "height": 168}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -56, "right": 52, "top": -145, "bottom": 0}, "height": 145}, {"frame": 1, "bounds": {"left": -16, "right": 72, "top": -145, "bottom": 0}, "height": 145}, {"frame": 2, "bounds": {"left": -66, "right": 50, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -26, "right": 63, "top": -139, "bottom": 0}, "height": 139}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -15, "right": 71, "top": -150, "bottom": 0}, "height": 150}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -35, "right": 36, "top": -111, "bottom": 0}, "height": 111}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -36, "right": 58, "top": -147, "bottom": 0}, "height": 147}, {"frame": 1, "bounds": {"left": -32, "right": 55, "top": -132, "bottom": 0}, "height": 132}, {"frame": 2, "bounds": {"left": -37, "right": 51, "top": -135, "bottom": 0}, "height": 135}, {"frame": 3, "bounds": {"left": -35, "right": 54, "top": -146, "bottom": 0}, "height": 146}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -46, "right": 49, "top": -162, "bottom": 0}, "height": 162}, {"frame": 1, "bounds": {"left": -47, "right": 58, "top": -161, "bottom": 0}, "height": 161}, {"frame": 2, "bounds": {"left": -50, "right": 78, "top": -162, "bottom": 0}, "height": 162}, {"frame": 3, "bounds": {"left": -48, "right": 51, "top": -162, "bottom": 0}, "height": 162}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -44, "right": 51, "top": -158, "bottom": 0}, "height": 158}, {"frame": 1, "bounds": {"left": -45, "right": 61, "top": -158, "bottom": 0}, "height": 158}, {"frame": 2, "bounds": {"left": -48, "right": 74, "top": -159, "bottom": 0}, "height": 159}, {"frame": 3, "bounds": {"left": -46, "right": 53, "top": -159, "bottom": 0}, "height": 159}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 52, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -51, "right": 83, "top": -147, "bottom": 0}, "height": 147}, {"frame": 2, "bounds": {"left": -58, "right": 62, "top": -124, "bottom": 0}, "height": 124}, {"frame": 3, "bounds": {"left": -33, "right": 40, "top": -163, "bottom": 0}, "height": 163}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -53, "right": 45, "top": -138, "bottom": 0}, "height": 138}, {"frame": 1, "bounds": {"left": -46, "right": 47, "top": -141, "bottom": 0}, "height": 141}, {"frame": 2, "bounds": {"left": -45, "right": 75, "top": -139, "bottom": 0}, "height": 139}, {"frame": 3, "bounds": {"left": -42, "right": 61, "top": -135, "bottom": 0}, "height": 135}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 52, "top": -182, "bottom": 0}, "height": 182}, {"frame": 1, "bounds": {"left": -47, "right": 52, "top": -182, "bottom": 0}, "height": 182}, {"frame": 2, "bounds": {"left": -48, "right": 52, "top": -182, "bottom": 0}, "height": 182}, {"frame": 3, "bounds": {"left": -49, "right": 81, "top": -182, "bottom": 0}, "height": 182}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -46, "right": 50, "top": -111, "bottom": 0}, "height": 111}, {"frame": 1, "bounds": {"left": -58, "right": 57, "top": -141, "bottom": 0}, "height": 141}, {"frame": 2, "bounds": {"left": -53, "right": 46, "top": -143, "bottom": 0}, "height": 143}, {"frame": 3, "bounds": {"left": -57, "right": 47, "top": -154, "bottom": 0}, "height": 154}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 48, "top": -157, "bottom": 0}, "height": 157}, {"frame": 1, "bounds": {"left": -76, "right": 50, "top": -156, "bottom": 0}, "height": 156}, {"frame": 2, "bounds": {"left": -39, "right": 50, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -40, "right": 46, "top": -152, "bottom": 0}, "height": 152}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -64, "right": 39, "top": -119, "bottom": 0}, "height": 119}, {"frame": 1, "bounds": {"left": -71, "right": 43, "top": -83, "bottom": 0}, "height": 83}, {"frame": 2, "bounds": {"left": -51, "right": 73, "top": -64, "bottom": 0}, "height": 64}, {"frame": 3, "bounds": {"left": -72, "right": 63, "top": -44, "bottom": 0}, "height": 44}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -78, "right": 67, "top": -57, "bottom": 0}, "height": 57}, {"frame": 1, "bounds": {"left": -61, "right": 43, "top": -93, "bottom": 0}, "height": 93}, {"frame": 2, "bounds": {"left": -38, "right": 43, "top": -100, "bottom": 0}, "height": 100}, {"frame": 3, "bounds": {"left": -41, "right": 43, "top": -128, "bottom": 0}, "height": 128}]}}, "emitters": {"attack1": {"x": 78, "y": -109, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 74, "y": -106, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 62, "y": -40, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 75, "y": -121, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 52, "y": -55, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 46, "y": -58, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 28, "y": -75}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 0, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE ASAM", "label": "ASAM", "announce": "MODE ASAM!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#9dff3c", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["JAB BERASAM", "SEMPROT ASAM", "GENANGAN LUAS", "BADAI ASAM"], "skills": {"skill1": {"kind": "wave", "damage": 20, "speed": 680, "range": 580, "size": 320, "tall": 200, "knockback": 220, "duration": 0.6, "hitAt": 0.5, "asset": "acidspray", "pierce": true}, "skill2": {"kind": "lob", "damage": 18, "rise": 760, "gravity": 1800, "radius": 190, "dps": 9, "puddleLife": 5, "puddle": "bigpuddle", "asset": "flask", "size": 110, "knockback": 200, "duration": 0.62, "hitAt": 0.55}}, "ultimate": {"kind": "rain", "asset": "drop", "count": 22, "gap": 0.09, "damage": 4, "radius": 125, "spread": 560, "size": 170, "delay": 0.4, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 34, "asset": "burst"}}, "cutin": {"a": "#031a03", "b": "#4aa21c", "kicker": "#e4ffb8", "title": "BADAI
ASAM", "detail": "THE ACID STORM"}}};
+window.FIGHTERS.sonofsolo={"id": "sonofsolo", "name": "SON OF SOLO MAN", "tag": "THE ACID HEIR", "race": "HUMAN", "title": "ACID HEIR", "color": "#7bd23c", "status": "COOL HEADED", "select": "assets/menu/sonofsolo-select.webp", "detail": "A calm stare, a flask, and an acid storm.", "style": "Zoner / acid flask and shouts", "balance": {"cooldowns": {"skill1": 4, "skill2": 7, "ultimate": 22}, "castTime": 0.5, "basicRefund": 0.05}, "names": ["JAB CEPAT", "ASAM SULFAT", "TERIAKAN", "HUJAN ASAM"], "combo": [{"damage": 6, "knockback": 80, "reach": 105, "duration": 0.34, "hitAt": 0.5}, {"damage": 8, "knockback": 110, "reach": 105, "duration": 0.38, "hitAt": 0.5}, {"damage": 12, "knockback": 220, "reach": 120, "duration": 0.5, "hitAt": 0.58}], "skills": {"skill1": {"kind": "lob", "damage": 14, "rise": 700, "gravity": 1800, "radius": 115, "dps": 6, "puddleLife": 3.5, "puddle": "puddle", "asset": "flask", "size": 90, "knockback": 160, "duration": 0.6, "hitAt": 0.55}, "skill2": {"kind": "projectile", "damage": 15, "speed": 980, "range": 560, "size": 170, "knockback": 200, "duration": 0.6, "hitAt": 0.5, "asset": "soundwave", "fx": "shout"}}, "ultimate": {"kind": "rain", "asset": "drop", "count": 14, "gap": 0.12, "damage": 3, "radius": 105, "spread": 380, "size": 150, "delay": 0.4, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 28, "asset": "burst"}}, "fx": ["acidspray", "bigpuddle", "burst", "drop", "flask", "impact", "puddle", "soundwave"], "cutin": {"a": "#0b1d0b", "b": "#2f6b1c", "kicker": "#d6ffb0", "title": "HUJAN
ASAM", "detail": "ACID DOWNPOUR"}, "manifest": {"characterId": "sonofsolo", "engine": "component-row", "game_input": "sprite-sheet-alpha.png", "degraded_static_fallback": false, "curation_applied": false, "frame_variant": "pixel", "sprite_sheet_alpha": "sprite-sheet-alpha.webp", "sprite_sheet_alpha_report": "sprite-sheet-alpha.report.json", "base_image": "base-source.png", "cell": {"shape": "rect", "width": 320, "height": 288, "safe_margin_x": 10, "safe_margin_y": 8}, "chroma_key": {"name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255], "selection": "manual"}, "animation": {"cellWidth": 320, "cellHeight": 288, "columns": 4, "rows": {"idle": {"row": 0, "frames": 4, "fps": 5, "durations_ms": [200, 200, 200, 200], "loop": true, "frame_variant": "pixel"}, "walk": {"row": 1, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": true, "frame_variant": "pixel"}, "run": {"row": 2, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": true, "frame_variant": "pixel"}, "jump": {"row": 3, "frames": 1, "fps": 10, "durations_ms": [100], "loop": false, "frame_variant": "pixel"}, "doublejump": {"row": 4, "frames": 1, "fps": 12, "durations_ms": [83], "loop": false, "frame_variant": "pixel"}, "crouch": {"row": 5, "frames": 4, "fps": 8, "durations_ms": [125, 125, 125, 125], "loop": false, "frame_variant": "pixel"}, "attack1": {"row": 6, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack2": {"row": 7, "frames": 4, "fps": 13, "durations_ms": [77, 77, 77, 77], "loop": false, "frame_variant": "pixel"}, "attack3": {"row": 8, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill1": {"row": 9, "frames": 4, "fps": 11, "durations_ms": [91, 91, 91, 91], "loop": false, "frame_variant": "pixel"}, "skill2": {"row": 10, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}, "ultimate": {"row": 11, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "hurt": {"row": 12, "frames": 4, "fps": 9, "durations_ms": [111, 111, 111, 111], "loop": false, "frame_variant": "pixel"}, "down": {"row": 13, "frames": 4, "fps": 7, "durations_ms": [143, 143, 143, 143], "loop": false, "frame_variant": "pixel"}, "recover": {"row": 14, "frames": 4, "fps": 10, "durations_ms": [100, 100, 100, 100], "loop": false, "frame_variant": "pixel"}}}, "frame_layout": {"sheetWidth": 1280, "sheetHeight": 4320, "cellWidth": 320, "cellHeight": 288, "rows": {"idle": [{"x": 0, "y": 0, "w": 320, "h": 288}, {"x": 320, "y": 0, "w": 320, "h": 288}, {"x": 640, "y": 0, "w": 320, "h": 288}, {"x": 960, "y": 0, "w": 320, "h": 288}], "walk": [{"x": 0, "y": 288, "w": 320, "h": 288}, {"x": 320, "y": 288, "w": 320, "h": 288}, {"x": 640, "y": 288, "w": 320, "h": 288}, {"x": 960, "y": 288, "w": 320, "h": 288}], "run": [{"x": 0, "y": 576, "w": 320, "h": 288}, {"x": 320, "y": 576, "w": 320, "h": 288}, {"x": 640, "y": 576, "w": 320, "h": 288}, {"x": 960, "y": 576, "w": 320, "h": 288}], "jump": [{"x": 0, "y": 864, "w": 320, "h": 288}], "doublejump": [{"x": 0, "y": 1152, "w": 320, "h": 288}], "crouch": [{"x": 0, "y": 1440, "w": 320, "h": 288}, {"x": 320, "y": 1440, "w": 320, "h": 288}, {"x": 640, "y": 1440, "w": 320, "h": 288}, {"x": 960, "y": 1440, "w": 320, "h": 288}], "attack1": [{"x": 0, "y": 1728, "w": 320, "h": 288}, {"x": 320, "y": 1728, "w": 320, "h": 288}, {"x": 640, "y": 1728, "w": 320, "h": 288}, {"x": 960, "y": 1728, "w": 320, "h": 288}], "attack2": [{"x": 0, "y": 2016, "w": 320, "h": 288}, {"x": 320, "y": 2016, "w": 320, "h": 288}, {"x": 640, "y": 2016, "w": 320, "h": 288}, {"x": 960, "y": 2016, "w": 320, "h": 288}], "attack3": [{"x": 0, "y": 2304, "w": 320, "h": 288}, {"x": 320, "y": 2304, "w": 320, "h": 288}, {"x": 640, "y": 2304, "w": 320, "h": 288}, {"x": 960, "y": 2304, "w": 320, "h": 288}], "skill1": [{"x": 0, "y": 2592, "w": 320, "h": 288}, {"x": 320, "y": 2592, "w": 320, "h": 288}, {"x": 640, "y": 2592, "w": 320, "h": 288}, {"x": 960, "y": 2592, "w": 320, "h": 288}], "skill2": [{"x": 0, "y": 2880, "w": 320, "h": 288}, {"x": 320, "y": 2880, "w": 320, "h": 288}, {"x": 640, "y": 2880, "w": 320, "h": 288}, {"x": 960, "y": 2880, "w": 320, "h": 288}], "ultimate": [{"x": 0, "y": 3168, "w": 320, "h": 288}, {"x": 320, "y": 3168, "w": 320, "h": 288}, {"x": 640, "y": 3168, "w": 320, "h": 288}, {"x": 960, "y": 3168, "w": 320, "h": 288}], "hurt": [{"x": 0, "y": 3456, "w": 320, "h": 288}, {"x": 320, "y": 3456, "w": 320, "h": 288}, {"x": 640, "y": 3456, "w": 320, "h": 288}, {"x": 960, "y": 3456, "w": 320, "h": 288}], "down": [{"x": 0, "y": 3744, "w": 320, "h": 288}, {"x": 320, "y": 3744, "w": 320, "h": 288}, {"x": 640, "y": 3744, "w": 320, "h": 288}, {"x": 960, "y": 3744, "w": 320, "h": 288}], "recover": [{"x": 0, "y": 4032, "w": 320, "h": 288}, {"x": 320, "y": 4032, "w": 320, "h": 288}, {"x": 640, "y": 4032, "w": 320, "h": 288}, {"x": 960, "y": 4032, "w": 320, "h": 288}]}}}, "metrics": {"anchor": {"x": 160, "y": 280}, "states": {"idle": {"frames": [{"frame": 0, "bounds": {"left": -33, "right": 38, "top": -180, "bottom": 0}, "height": 180}, {"frame": 1, "bounds": {"left": -33, "right": 38, "top": -183, "bottom": 0}, "height": 183}, {"frame": 2, "bounds": {"left": -33, "right": 38, "top": -180, "bottom": 0}, "height": 180}, {"frame": 3, "bounds": {"left": -32, "right": 39, "top": -180, "bottom": 0}, "height": 180}]}, "walk": {"frames": [{"frame": 0, "bounds": {"left": -42, "right": 45, "top": -167, "bottom": 0}, "height": 167}, {"frame": 1, "bounds": {"left": -27, "right": 37, "top": -170, "bottom": 0}, "height": 170}, {"frame": 2, "bounds": {"left": -42, "right": 45, "top": -168, "bottom": 0}, "height": 168}, {"frame": 3, "bounds": {"left": -26, "right": 45, "top": -168, "bottom": 0}, "height": 168}]}, "run": {"frames": [{"frame": 0, "bounds": {"left": -56, "right": 52, "top": -145, "bottom": 0}, "height": 145}, {"frame": 1, "bounds": {"left": -16, "right": 72, "top": -145, "bottom": 0}, "height": 145}, {"frame": 2, "bounds": {"left": -66, "right": 50, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -26, "right": 63, "top": -139, "bottom": 0}, "height": 139}]}, "jump": {"frames": [{"frame": 0, "bounds": {"left": -15, "right": 71, "top": -150, "bottom": 0}, "height": 150}]}, "doublejump": {"frames": [{"frame": 0, "bounds": {"left": -35, "right": 36, "top": -111, "bottom": 0}, "height": 111}]}, "crouch": {"frames": [{"frame": 0, "bounds": {"left": -36, "right": 58, "top": -147, "bottom": 0}, "height": 147}, {"frame": 1, "bounds": {"left": -32, "right": 55, "top": -132, "bottom": 0}, "height": 132}, {"frame": 2, "bounds": {"left": -37, "right": 51, "top": -135, "bottom": 0}, "height": 135}, {"frame": 3, "bounds": {"left": -35, "right": 54, "top": -146, "bottom": 0}, "height": 146}]}, "attack1": {"frames": [{"frame": 0, "bounds": {"left": -46, "right": 49, "top": -162, "bottom": 0}, "height": 162}, {"frame": 1, "bounds": {"left": -47, "right": 58, "top": -161, "bottom": 0}, "height": 161}, {"frame": 2, "bounds": {"left": -50, "right": 78, "top": -162, "bottom": 0}, "height": 162}, {"frame": 3, "bounds": {"left": -48, "right": 51, "top": -162, "bottom": 0}, "height": 162}]}, "attack2": {"frames": [{"frame": 0, "bounds": {"left": -44, "right": 51, "top": -158, "bottom": 0}, "height": 158}, {"frame": 1, "bounds": {"left": -45, "right": 61, "top": -158, "bottom": 0}, "height": 158}, {"frame": 2, "bounds": {"left": -48, "right": 74, "top": -159, "bottom": 0}, "height": 159}, {"frame": 3, "bounds": {"left": -46, "right": 53, "top": -159, "bottom": 0}, "height": 159}]}, "attack3": {"frames": [{"frame": 0, "bounds": {"left": -48, "right": 52, "top": -146, "bottom": 0}, "height": 146}, {"frame": 1, "bounds": {"left": -51, "right": 83, "top": -147, "bottom": 0}, "height": 147}, {"frame": 2, "bounds": {"left": -58, "right": 62, "top": -124, "bottom": 0}, "height": 124}, {"frame": 3, "bounds": {"left": -33, "right": 40, "top": -163, "bottom": 0}, "height": 163}]}, "skill1": {"frames": [{"frame": 0, "bounds": {"left": -53, "right": 45, "top": -138, "bottom": 0}, "height": 138}, {"frame": 1, "bounds": {"left": -46, "right": 47, "top": -141, "bottom": 0}, "height": 141}, {"frame": 2, "bounds": {"left": -45, "right": 75, "top": -139, "bottom": 0}, "height": 139}, {"frame": 3, "bounds": {"left": -42, "right": 61, "top": -135, "bottom": 0}, "height": 135}]}, "skill2": {"frames": [{"frame": 0, "bounds": {"left": -47, "right": 52, "top": -182, "bottom": 0}, "height": 182}, {"frame": 1, "bounds": {"left": -47, "right": 52, "top": -182, "bottom": 0}, "height": 182}, {"frame": 2, "bounds": {"left": -48, "right": 52, "top": -182, "bottom": 0}, "height": 182}, {"frame": 3, "bounds": {"left": -49, "right": 81, "top": -182, "bottom": 0}, "height": 182}]}, "ultimate": {"frames": [{"frame": 0, "bounds": {"left": -46, "right": 50, "top": -111, "bottom": 0}, "height": 111}, {"frame": 1, "bounds": {"left": -58, "right": 57, "top": -141, "bottom": 0}, "height": 141}, {"frame": 2, "bounds": {"left": -53, "right": 46, "top": -143, "bottom": 0}, "height": 143}, {"frame": 3, "bounds": {"left": -57, "right": 47, "top": -154, "bottom": 0}, "height": 154}]}, "hurt": {"frames": [{"frame": 0, "bounds": {"left": -51, "right": 48, "top": -157, "bottom": 0}, "height": 157}, {"frame": 1, "bounds": {"left": -76, "right": 50, "top": -156, "bottom": 0}, "height": 156}, {"frame": 2, "bounds": {"left": -39, "right": 50, "top": -146, "bottom": 0}, "height": 146}, {"frame": 3, "bounds": {"left": -40, "right": 46, "top": -152, "bottom": 0}, "height": 152}]}, "down": {"frames": [{"frame": 0, "bounds": {"left": -64, "right": 39, "top": -119, "bottom": 0}, "height": 119}, {"frame": 1, "bounds": {"left": -71, "right": 43, "top": -83, "bottom": 0}, "height": 83}, {"frame": 2, "bounds": {"left": -51, "right": 73, "top": -64, "bottom": 0}, "height": 64}, {"frame": 3, "bounds": {"left": -72, "right": 63, "top": -44, "bottom": 0}, "height": 44}]}, "recover": {"frames": [{"frame": 0, "bounds": {"left": -78, "right": 67, "top": -57, "bottom": 0}, "height": 57}, {"frame": 1, "bounds": {"left": -61, "right": 43, "top": -93, "bottom": 0}, "height": 93}, {"frame": 2, "bounds": {"left": -38, "right": 43, "top": -100, "bottom": 0}, "height": 100}, {"frame": 3, "bounds": {"left": -41, "right": 43, "top": -128, "bottom": 0}, "height": 128}]}}, "emitters": {"attack1": {"x": 78, "y": -109, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack2": {"x": 74, "y": -106, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "attack3": {"x": 62, "y": -40, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill1": {"x": 75, "y": -121, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "skill2": {"x": 52, "y": -55, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}, "ultimate": {"x": 46, "y": -58, "frame": 2, "method": "Farthest-right opaque tip on the impact frame, facing right"}}, "playback": {"jump": {"airFrame": 0, "torsoPivot": {"x": 28, "y": -75}}, "doublejump": {"frame": 0, "duration": 0.28, "pivot": {"x": 0, "y": -56}}, "measurement": "Measured from the final atlas; no runtime resizing."}}, "mode": {"name": "MODE ASAM", "label": "ASAM", "announce": "MODE ASAM!", "duration": 10, "damageMul": 1.35, "speedMul": 1.2, "reachAdd": 20, "aura": "#9dff3c", "cooldowns": {"skill1": 5, "skill2": 8}, "names": ["JAB BERASAM", "SEMPROT ASAM", "GENANGAN LUAS", "BADAI ASAM"], "skills": {"skill1": {"kind": "wave", "damage": 14, "speed": 680, "range": 580, "size": 320, "tall": 200, "knockback": 220, "duration": 0.6, "hitAt": 0.5, "asset": "acidspray", "pierce": true, "fx": "spray", "dot": {"dps": 3, "time": 2.5}}, "skill2": {"kind": "lob", "damage": 18, "rise": 760, "gravity": 1800, "radius": 190, "dps": 9, "puddleLife": 5, "puddle": "bigpuddle", "asset": "flask", "size": 110, "knockback": 200, "duration": 0.62, "hitAt": 0.55}}, "ultimate": {"kind": "rain", "asset": "drop", "count": 22, "gap": 0.09, "damage": 4, "radius": 125, "spread": 560, "size": 170, "delay": 0.4, "hold": 1.1, "settle": 0.6, "finisher": {"damage": 34, "asset": "burst"}}, "cutin": {"a": "#031a03", "b": "#4aa21c", "kicker": "#e4ffb8", "title": "BADAI
ASAM", "detail": "THE ACID STORM"}}};
diff --git a/docs/ENGINE_NOTES.md b/docs/ENGINE_NOTES.md
index 6d3a246..9899438 100644
--- a/docs/ENGINE_NOTES.md
+++ b/docs/ENGINE_NOTES.md
@@ -9,6 +9,7 @@ Game fighting 2D canvas, tanpa dependensi dan tanpa langkah build. Semua teks un
- Serangan: `startAttack`, `cast`/`castMove`, `meleeHit`, `fireSkill`, `hitDummy` (pemain memukul lawan), `window.__game.receiveHit` (lawan memukul pemain), `startCommand`/`specialHit`/`startThrow`/`useShopMove`/`heroGuard` (gerakan perintah, lempar, blok/parry, jurus toko).
- Karakter data-driven: `window.FIGHTERS[id]` (dari `assets//manifest.js`) → `cfKit(d)` (gerakan), `cfStrike(actor, def, move)` (efek pukul: jenis projectile/wave/homing/dash/area/pillar/lob; ultimate volley/stampede/rain/pillars via `updateCfUltimate`). Bro Wo ditulis tangan (`browo.js` + `updateBrowo`, `browoStrike`).
- Proyektil: array `projectiles`, diperbarui di `updateProjectiles(dt)`; efek visual: array `effects` (tipe `slash`, `impact`, `cf-fx`, `cf-pillar`, ...) digambar di `drawEffects()`; partikel `spawnParticles`; angka damage `damageNumbers`.
+ - Efek jurus (blok "Efek jurus (FX)" di game.js, dipakai pemain dan CPU lewat `cfStrike`): proyektil CF punya umur (`p.age`) dan gaya gambar `p.fx` (rings, shout, chain, roll, spray, flame, bull, drive, wave, bolt, ant); `fxPart` membuat partikel kosmetik (pengacak terpisah `fxr()`, bukan `random()`); jurus area punya aba-aba cincin 0,25 s (`fxTeleP`), gelombang kejut dan retakan; dash meninggalkan bayangan sprite (`cfx.ghosts`). Flag data di manifest per skill (juga `mode.skills`): `fx`, `lift`, `trail`, `split`, `dot`, `bind`, `convoy`, `aftershock`, `pull`, `rebound`, `swarm`+`ramp`, `fire`; artinya ada di komentar "Mekanik jurus" di game.js.
- CPU: `updateFenrAI`, `cpuProfile` (tingkat easy/medium/hard/excellent dari `match.js`).
- Aturan ronde: `match.js` (`MatchRules`), dipakai `advanceMatch` di game.js.
- `meta.js`: `window.Meta` — koin, toko, skin (filter warna), jurus toko (tombol K), varian skill, menu Karakter/Koleksi, pencapaian, easter egg, Arcade/Survival (`startRun`), kolom kode cheat di Pengaturan. Simpanan: `localStorage['konoha.save.v1']`.
@@ -20,8 +21,10 @@ Game fighting 2D canvas, tanpa dependensi dan tanpa langkah build. Semua teks un
## API uji: `window.__game`
`startMatch({player,enemy,stage,level,mode})` (mode `versus`|`training`), `advance(detik)` (memajukan simulasi tanpa menunggu waktu nyata), `press(key)`/`release(key)` (a d w s spasi i o p k l j; P2: arrowleft arrowright arrowup n m , .), `setAI(bool)`, `setP2(bool)`, `snapshot()`, `hero`, `dummy`, `projectiles`, `effects`, `draw()`, `receiveHit(dmg)`, `command(id)`. Layar tidak diperbarui saat `advance`; panggil `draw()` sebelum screenshot.
+`__game.fx.snapshot()` melaporkan keadaan efek (bayangan, genangan api, DoT, aba-aba, proyektil beserta gaya/umur) untuk skenario uji. Untuk uji deterministik set `__game.manual = true` dulu; kalau tidak, loop waktu nyata ikut memajukan simulasi saat screenshot.
+
## Alat uji headless
-`node tests/harness.mjs smoke` — 7 karakter × easy/hard melawan CPU, gagal bila ada galat halaman. `node tests/harness.mjs tests/foo.mjs` — menjalankan skenario: `export default async ({ page, game, errors, characters }) => hasil` (`game(fn, arg)` = `page.evaluate`). Screenshot: `await page.screenshot({path})` lalu `Read` gambar. Node dan Playwright sudah terpasang; tidak perlu `npm install`.
+`node tests/harness.mjs smoke` — 7 karakter × easy/hard melawan CPU, gagal bila ada galat halaman. `node tests/harness.mjs tests/foo.mjs` — menjalankan skenario: `export default async ({ page, game, errors, characters }) => hasil` (`game(fn, arg)` = `page.evaluate`). Screenshot: `await page.screenshot({path})` lalu `Read` gambar. `tests/t1-fx.mjs` melepas semua jurus enam karakter berbasis data dari kedua sisi (env `FX_SHOTS` = folder screenshot, `FX_ONLY` = regex nama jurus). Node dan Playwright sudah terpasang; tidak perlu `npm install`.
## Aturan kerja
- Jangan memakai `Math.random`/`Date.now` untuk logika yang memengaruhi hasil pertandingan bila ada pengacak `random()` bawaan (benih tetap).
diff --git a/game.js b/game.js
index 8868dd1..567d541 100644
--- a/game.js
+++ b/game.js
@@ -836,7 +836,7 @@
// projectile | wave | homing | dash | area | pillar | lob (skill1 / skill2 / basic chain is melee)
// and the ultimate by `ultimate.kind`: volley | stampede | rain | pillars. All ultimates share the cinematic: the camera
// dives onto the caster under a dark screen, the rival is held, the beats land, then an optional finisher. See work/WORKFLOW.md.
- const cfx = { ents: [], hazards: [], marks: [] };
+ const cfx = { ents: [], hazards: [], marks: [], later: [], dots: [], ghosts: [] };
function cfKit(d) {
const move = (name, index = 1, mode = false) => {
if (name === 'attack') { const c = d.combo[index - 1]; return { name: `attack${index}`, type: 'attack', index, ...c, reach: Math.round((c.reach || 105) * (CONFIG.reachScale || 1)) }; }
@@ -869,30 +869,44 @@
move = { ...move, damage: Math.round((move.damage || 0) * mul), reach: (move.reach || 0) + radd };
const k = move.kind;
if (k === 'projectile' || k === 'wave' || k === 'homing') {
- const n = move.count || 1, ox = actor.x + actor.facing * Math.round((em?.x ?? 100) * .7);
- for (let i = 0; i < n; i++) {
- const off = (i - (n - 1) / 2) * (move.spread || 0), p = { x: ox, y: actor.y + (move.y ?? -95) + (k === 'homing' ? 0 : off), vx: actor.facing * move.speed, life: move.life || (move.range || 560) / move.speed, facing: actor.facing, owner, damage: move.damage, knockback: move.knockback, asset: key(move.asset), size: move.size || 150, color: col, tall: k === 'wave' ? (move.tall || 170) : move.tall, pass: !!move.pierce };
+ const n = move.count || 1, ox = actor.x + actor.facing * Math.round((em?.x ?? 100) * .7), cv = move.convoy, grp = { hits: 0 };
+ for (let c = 0; c < (cv?.n || 1); c++) for (let i = 0; i < n; i++) {
+ const off = (i - (n - 1) / 2) * (move.spread || 0), p = { x: ox, y: actor.y + (move.y ?? -95) + (k === 'homing' ? 0 : off) + (cv ? (c - 1) * 12 : 0), vx: actor.facing * move.speed, life: move.life || (move.range || 560) / move.speed, facing: actor.facing, owner, damage: cv ? Math.max(1, Math.round(move.damage * (cv.mul || .36))) : move.damage, knockback: move.knockback, asset: key(move.asset), size: move.size || 150, color: col, tall: k === 'wave' ? (move.tall || 170) : move.tall, pass: !!move.pierce };
if (k === 'homing') { p.vy = off - 60; p.vx = actor.facing * move.speed * .5; p.homing = true; p.speedH = move.speed; p.turn = move.turn || 5; p.tall = move.tall || 90; }
+ fxProj(p, move);
+ if (cv && c) p.wait = c * (cv.gap || .14); // convoy: setiap mobil menunggu giliran di titik awal
+ if (move.swarm) { // semut mengitari penyihir dulu (arah putar acak per semut), lalu dilepas satu per satu mengejar lawan
+ const S = move.swarm, release = (S.orbit || .55) + i * (S.stagger || .05); p.chase = move.life || 2.4;
+ p.sw = { ang: i / n * 6.2832, w: (i % 2 ? 1 : -1) * (8 + (i % 3) * 1.5), R: (S.radius || 90) + (i % 3) * 16, release, go: false, speed: move.speed, turn: move.turn || 5, chase: p.chase, hd: 0 };
+ p.homing = false; p.vx = p.vy = 0; p.life = release + p.chase + .2; p.life0 = p.life;
+ if (move.ramp) { p.ramp = { base: move.damage, every: move.ramp.every, add: move.ramp.add }; p.grp = grp; }
+ }
projectiles.push(p);
}
spawnParticles(ox, actor.y + (move.y ?? -95), col, 10, 160); sound('cast'); return;
}
if (k === 'lob') {
const T = .85, vx = clamp((target.x - actor.x) / T, -720, 720);
- projectiles.push({ x: actor.x + actor.facing * 30, y: actor.y - 110, vx, vy: -(move.rise || 700), cfG: move.gravity || 1800, cfLand: { asset: move.puddle, radius: move.radius || 110, dps: move.dps || 6, life: move.puddleLife || 3.5, burst: move.damage }, life: 3, facing: vx >= 0 ? 1 : -1, owner, damage: Math.round(move.damage * .5), knockback: move.knockback, asset: key(move.asset), size: move.size || 90, color: col, tall: 80, pass: true });
+ projectiles.push({ x: actor.x + actor.facing * 30, y: actor.y - 110, vx, vy: -(move.rise || 700), cfG: move.gravity || 1800, cfLand: { asset: move.puddle, radius: move.radius || 110, dps: move.dps || 6, life: move.puddleLife || 3.5, burst: move.damage }, spin: 9, life: 3, facing: vx >= 0 ? 1 : -1, owner, damage: Math.round(move.damage * .5), knockback: move.knockback, asset: key(move.asset), size: move.size || 90, color: col, tall: 80, pass: true });
sound('cast'); return;
}
let hitX = null, inside;
if (k === 'dash') {
effects.push({ type: 'cf-fx', asset: key(move.asset || 'impact'), x: actor.x + actor.facing * 60, y: actor.y - 80, facing: actor.facing, life: .3, maxLife: .3, size: move.size || 200 });
const dx = (target.x - actor.x) * actor.facing; inside = dx > -22 && dx < (move.reach || 120) + 27 && Math.abs(target.y - actor.y) <= 115; hitX = actor.x;
+ // rebound: banteng api menembus lalu memantul kembali (proyektil pulang-pergi, satu pukulan tiap arah)
+ // (spent: lawan yang sudah kena dash tidak dipukul lagi di jalan keluar, hanya saat banteng memantul kembali)
+ if (move.rebound) { const R = move.rebound, rg = Math.max(150, (target.x - actor.x) * actor.facing - 50 + R.range), rp = { x: actor.x + actor.facing * 50, y: actor.y - 95, vx: actor.facing * (R.speed || 1000), life: 2 * rg / (R.speed || 1000) + .8, facing: actor.facing, owner, damage: Math.round(R.damage * mul), knockback: (move.knockback || 160) * .7, asset: key(R.asset || move.asset), size: R.size || 280, color: col, tall: 100, pass: true, spent: inside, reb: { range: rg, back: false } }; fxProj(rp, { fx: 'bull', kind: 'rebound' }); projectiles.push(rp); }
} else if (k === 'area' || k === 'pillar') {
hitX = actor.x + actor.facing * (move.ahead || 0);
if (k === 'pillar') effects.push({ type: 'cf-pillar', img: 'fx-' + key(move.asset), x: hitX, y: CONFIG.groundY, life: .9, maxLife: .9, h: move.h || 330 });
else effects.push({ type: 'cf-fx', asset: key(move.asset || 'impact'), x: hitX, y: actor.y - 90, facing: 1, life: .5, maxLife: .5, size: move.size || (move.radius || 120) * 2.4 });
effects.push({ type: 'cf-fx', asset: key('impact'), x: hitX, y: CONFIG.groundY - 24, facing: 1, life: .4, maxLife: .4, size: 220 });
dust(hitX, 14); spawnParticles(hitX, CONFIG.groundY - 10, col, 14, 280, 'dust'); trauma = Math.min(1, trauma + .25);
+ if (k === 'area') fxAreaBlast(hitX, move.radius || 100, col, false);
inside = Math.abs(target.x - hitX) < (move.radius || 100) && Math.abs(target.y - actor.y) <= (move.vy || 150);
+ // aftershock: ledakan kedua beberapa saat kemudian di titik yang sama, radius lebih besar (menjangkau yang terlempar/menjauh)
+ if (move.aftershock) { const A = move.aftershock; cfx.later.push({ t: A.delay || .22, fn: () => { fxAreaBlast(hitX, A.radius, col, true); trauma = Math.min(1, trauma + .2); sound('heavy'); if (bwAlive(target) && Math.abs(target.x - hitX) < A.radius && target.y > CONFIG.groundY - 140) hit(Math.round(A.damage * mul), move.knockback, hitX, hy, false); } }); }
} else { // basic chain: a plain melee hit in front
effects.push({ type: 'slash', x: actor.x + actor.facing * 55, y: hy + 5, life: .24, maxLife: .24, facing: actor.facing, index: move.index, color: col });
if (move.index === 3) { dust(actor.x + actor.facing * (em?.x ?? 95), 12); trauma = Math.min(1, trauma + .15); }
@@ -915,6 +929,7 @@
if (bwAlive(target) && Math.abs(target.x - p.x) < L.radius && target.y > CONFIG.groundY - 120) dotDamage(target, L.burst, p.color);
}
function updateCf(dt) {
+ fxTick(dt);
// Entities (ultimate volleys and stampedes): move, then land one hit each on the rival they cross.
for (let n = cfx.ents.length - 1; n >= 0; n--) {
const e = cfx.ents[n], target = e.owner === 'enemy' ? hero : dummy;
@@ -938,17 +953,17 @@
}
}
// Puddles: one burn per rival, however many puddles they stand in.
- const inside = { player: false, enemy: false }, hz = {};
+ const inside = { player: false, enemy: false }, hz = {}, hcol = {};
for (let n = cfx.hazards.length - 1; n >= 0; n--) {
const h = cfx.hazards[n], target = h.owner === 'enemy' ? hero : dummy, tk = h.owner === 'enemy' ? 'player' : 'enemy'; h.t += dt;
- if (bwAlive(target) && Math.abs(target.x - h.x) < h.radius && target.y > CONFIG.groundY - 110) { inside[tk] = true; hz[tk] = Math.max(hz[tk] || 0, h.dps); }
+ if (bwAlive(target) && Math.abs(target.x - h.x) < h.radius && target.y > CONFIG.groundY - 110) { inside[tk] = true; hz[tk] = Math.max(hz[tk] || 0, h.dps); hcol[tk] = h.color || '#9bd05a'; }
if (h.t > h.life) cfx.hazards.splice(n, 1);
}
for (const k of ['player', 'enemy']) {
if (!inside[k]) continue;
const target = k === 'player' ? hero : dummy; bwx.burn[k] += hz[k] * dt;
- if (bwx.burn[k] >= 1) { const m = Math.floor(bwx.burn[k]); bwx.burn[k] -= m; bwx.burn.shown[k] += m; dotDamage(target, m, '#9bd05a', false); }
- if (bwx.burn.shown[k] >= 3) { damageNumbers.push({ x: target.x + (random() - .5) * 24, y: target.y - 130, amount: bwx.burn.shown[k], life: .9, maxLife: .9, color: '#9bd05a' }); bwx.burn.shown[k] = 0; }
+ if (bwx.burn[k] >= 1) { const m = Math.floor(bwx.burn[k]); bwx.burn[k] -= m; bwx.burn.shown[k] += m; dotDamage(target, m, hcol[k], false); }
+ if (bwx.burn.shown[k] >= 3) { damageNumbers.push({ x: target.x + (random() - .5) * 24, y: target.y - 130, amount: bwx.burn.shown[k], life: .9, maxLife: .9, color: hcol[k] }); bwx.burn.shown[k] = 0; }
}
}
// The ultimate director. s.t counts from the cast; beats start after `hold` seconds of camera dive.
@@ -1012,7 +1027,15 @@
const t = 1 - e.life / e.maxLife, k = Math.min(1, t * 7) * (t > .7 ? 1 - (t - .7) / .3 : 1), h = e.h, w = im.width / im.height * h;
ctx.save(); ctx.translate(Math.round(e.x), Math.round(e.y)); ctx.beginPath(); ctx.rect(-w, -h - 10, w * 2, h + 10); ctx.clip(); ctx.drawImage(im, -w / 2, -h * k, w, h); ctx.restore();
}
+ for (const e of effects) { // retakan tanah dari jurus area
+ if (e.type !== 'cf-crack') continue; const t = 1 - e.life / e.maxLife, a = Math.min(1, t * 12) * (t > .6 ? (1 - t) / .4 : 1);
+ ctx.save(); ctx.translate(Math.round(e.x), e.y); ctx.lineCap = 'round'; ctx.lineJoin = 'round';
+ for (const [col, wd, al, oy] of [['#2a1a10', 3.2, .85, 0], ['#f2e2c0', 1.2, .4, -1.5]]) { ctx.strokeStyle = col; ctx.lineWidth = wd; ctx.globalAlpha = a * al; ctx.beginPath(); for (const pts of e.segs) { ctx.moveTo(pts[0][0], pts[0][1] + oy); for (let j = 1; j < pts.length; j++) ctx.lineTo(pts[j][0], pts[j][1] + oy); } ctx.stroke(); }
+ ctx.restore();
+ }
+ fxDrawTele(hero, CF[selectedCharacter]?.color || '#ffd36a'); fxDrawTele(dummy, CF[opponentCharacter]?.color || '#ffd36a'); fxDrawGhosts(); fxDrawDash(hero, CF[selectedCharacter]); fxDrawDash(dummy, CF[opponentCharacter]);
for (const h of cfx.hazards) {
+ if (h.fire) { fxDrawFire(h); continue; }
const im = images[h.img]; if (!im) continue; const fade = h.t > h.life - .6 ? Math.max(0, (h.life - h.t) / .6) : 1, w = h.radius * 2.4, hh = im.height / im.width * w;
ctx.save(); ctx.globalAlpha = .9 * fade; ctx.drawImage(im, Math.round(h.x - w / 2), CONFIG.groundY + 6 - hh, w, hh); ctx.restore();
}
@@ -1029,12 +1052,331 @@
ctx.restore();
}
}
+ // ======== Efek jurus (FX): mesin efek bersama untuk jurus berbasis data ==============================================
+ // Semua canvas 2D, tanpa gambar baru. Proyektil/gelombang punya umur (p.age): muncul membesar dengan overshoot, memanjang
+ // sesuai kecepatan, irisan vertikal bergoyang, ekor bayangan + percikan, memudar di akhir hidup, pencampuran cahaya per gaya
+ // (p.fx = rings | shout | chain | roll | spray | drive | wave | flame | bolt | ant). Jurus area: cincin aba-aba, gelombang
+ // kejut, retakan. Dash: bayangan sprite, debu, garis kecepatan. Jurus mode: flag data di manifest (lihat cfStrike).
+ // Pengacak kosmetik terpisah dari random() agar hasil pertandingan tidak berubah oleh efek.
+ let fxSeed = 90210;
+ function fxr() { fxSeed = (Math.imul(fxSeed, 1103515245) + 12345) >>> 0; return fxSeed / 4294967296; }
+ const easeOutBack = t => 1 + 2.70158 * Math.pow(t - 1, 3) + 1.70158 * Math.pow(t - 1, 2);
+ const FX_GLYPHS = ['A', 'H', '!', '?', 'W', 'O', '#', 'E'];
+ function fxPart(x, y, vx, vy, life, size, color, kind, extra) {
+ if (particles.length > 460) return;
+ particles.push({ x, y, vx, vy, life, maxLife: life, size, color, kind, fade: true, rot0: fxr() * 6.28, spin: (fxr() - .5) * 9, ...extra });
+ }
+ function fxDrawPart(p) {
+ const k = clamp(p.life / p.maxLife, 0, 1), x = Math.round(p.x), y = Math.round(p.y);
+ if (p.kind === 'foam') { ctx.fillStyle = p.color; ctx.beginPath(); ctx.arc(x, y, p.size * (.45 + .55 * k), 0, 7); ctx.fill(); }
+ else if (p.kind === 'bubble') { ctx.strokeStyle = p.color; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(x, y, p.size * (1.2 - .4 * k), 0, 7); ctx.stroke(); }
+ else if (p.kind === 'glyph') { ctx.font = `900 ${p.size}px Rajdhani, sans-serif`; ctx.textAlign = 'center'; ctx.lineWidth = 4; ctx.strokeStyle = p.edge || '#10243c'; ctx.translate(x, y); ctx.rotate(p.rot0 * .2 + (1 - k) * p.spin * .4); ctx.strokeText(p.ch, 0, 0); ctx.fillStyle = p.color; ctx.fillText(p.ch, 0, 0); }
+ else if (p.kind === 'streak') { const m = Math.hypot(p.vx, p.vy) || 1, L = p.len * (.4 + .6 * k); ctx.strokeStyle = p.color; ctx.lineWidth = p.size; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x - p.vx / m * L, y - p.vy / m * L); ctx.stroke(); }
+ else if (p.kind === 'ember') { ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = p.color; ctx.beginPath(); ctx.arc(x, y, p.size * k * .6 + .8, 0, 7); ctx.fill(); }
+ else if (p.kind === 'chip') { ctx.translate(x, y); ctx.rotate(p.rot0 + (1 - k) * p.spin * 2); ctx.fillStyle = p.color; ctx.fillRect(-p.size / 2, -p.size / 3, p.size, p.size * .66); }
+ else ctx.fillRect(x, y, Math.max(1, Math.round(p.size)), Math.max(1, Math.round(p.size)));
+ }
+ // Projectile set-up, called by cfStrike for every projectile it creates: gives it a life clock, a trail and its flags.
+ const FX_FLAGS = ['lift', 'dot', 'bind', 'trail', 'split', 'fire'];
+ function fxProj(p, move) {
+ p.fx = move.fx || (move.kind === 'wave' ? 'wave' : move.kind === 'homing' ? 'ant' : 'bolt');
+ p.age = 0; p.life0 = p.life; p.tr = []; p.acc = 0; p.sx0 = p.x;
+ for (const f of FX_FLAGS) if (move[f]) p[f] = move[f];
+ }
+ // One simulation step of a FX projectile's cosmetics and special behaviour. Returns 'remove' / 'skip' (no collision this step).
+ function fxProjTick(p, dt) {
+ p.age += dt; p.acc += dt;
+ const f = p.facing || 1, gy = CONFIG.groundY;
+ if (p.age - (p.trT || 0) >= .045) { p.trT = p.age; p.tr.push({ x: p.x, y: p.y }); if (p.tr.length > 4) p.tr.shift(); }
+ const mr = fxMech(p, dt); if (mr) return mr;
+ if (p.acc >= .03) {
+ p.acc -= .03; const sz = p.size || 150;
+ if (p.fx === 'shout') {
+ fxPart(p.x - f * 18, p.y + (fxr() - .5) * 46, -f * (90 + fxr() * 200), (fxr() - .5) * 260, .42 + fxr() * .25, 15 + fxr() * 14, fxr() < .5 ? '#d8f1ff' : '#7fc4ff', 'glyph', { ch: FX_GLYPHS[Math.floor(fxr() * FX_GLYPHS.length)], g: 260, drag: 1.5 });
+ fxPart(p.x - f * 8, p.y + (fxr() - .5) * 40, -f * (60 + fxr() * 160), (fxr() - .5) * 200, .3, 3 + fxr() * 3, '#bfe6ff', 'ember', {});
+ } else if (p.fx === 'rings') {
+ const a = (fxr() - .5) * 1.9;
+ fxPart(p.x + f * sz * .22 * Math.cos(a), p.y + sz * .3 * Math.sin(a), f * (30 + fxr() * 90), (fxr() - .5) * 120, .35, 4 + fxr() * 3, '#ffd9a0', 'ember', { g: 0, drag: 2 });
+ } else if (p.fx === 'chain') {
+ const k = Math.floor(fxr() * 5);
+ fxPart(p.x - f * k * sz * .17, p.y + (fxr() - .5) * 80, -f * (20 + fxr() * 60), -40 - fxr() * 90, .55, 14 + fxr() * 8, '#ffe9a6', 'glyph', { ch: fxr() < .5 ? '♪' : '♫', g: -20, drag: 1.5, edge: '#5a3a08' });
+ } else if (p.fx === 'roll') {
+ const grow = fxGrow(p), top = p.y + 95 - sz * grow * .55;
+ fxPart(p.x + f * (fxr() * sz * .35 - sz * .1), top + fxr() * sz * .25 * grow, f * (60 + fxr() * 140), -170 - fxr() * 170, .5 + fxr() * .25, 3 + fxr() * 4, p.fire ? '#ffb347' : '#e8fff6', p.fire ? 'ember' : 'foam', { g: 760, drag: 1, alpha: .85 });
+ if (fxr() < .5) fxPart(p.x - f * sz * .3 * fxr(), gy - 4, -f * (30 + fxr() * 50), -20 - fxr() * 40, .45, 7 + fxr() * 5, '#d8fff0', 'foam', { g: 120, drag: 2 });
+ } else if (p.fx === 'spray') {
+ fxPart(p.x + f * (fxr() - .3) * sz * .4, p.y + (fxr() - .5) * 60, f * (40 + fxr() * 140), (fxr() - .2) * 240, .5 + fxr() * .3, 4 + fxr() * 5, fxr() < .5 ? '#a6f04a' : '#d8ff7a', 'foam', { g: 700, drag: 1.2 });
+ } else if (p.fx === 'flame' || p.fx === 'bull') {
+ fxPart(p.x - f * fxr() * sz * .35, p.y + (fxr() - .5) * 70, -f * (20 + fxr() * 60), -60 - fxr() * 130, .5, 5 + fxr() * 6, fxr() < .5 ? '#ff9a3a' : '#ffd36a', 'ember', { g: -80, drag: 1.5 });
+ } else if (p.fx === 'drive') {
+ fxPart(p.x - f * sz * .38, gy - 6 - fxr() * 10, -f * (30 + fxr() * 80), -20 - fxr() * 60, .5, 8 + fxr() * 8, '#e7d2a8', 'foam', { g: -30, drag: 2, alpha: .4 });
+ } else if (p.fx === 'wave') {
+ fxPart(p.x - f * fxr() * sz * .3, p.y + (fxr() - .5) * 60, -f * (30 + fxr() * 80), (fxr() - .5) * 120, .4, 3 + fxr() * 3, p.color || '#fff', 'ember', { g: 0 });
+ } else if (p.fx === 'bolt') {
+ fxPart(p.x - f * 20, p.y + (fxr() - .5) * 36, -f * (30 + fxr() * 100), (fxr() - .5) * 140, .38, 3 + fxr() * 3, p.color || '#fff', 'ember', { g: 0 });
+ }
+ }
+ return null;
+ }
+ function fxGrow(p) { return .55 + .85 * clamp(p.age / ((p.life0 || 1) * .75), 0, 1); }
+ function fxStrips(im, w, h, n, amp, freq, phase, hk, dyk) {
+ const sw = im.width / n, dw = w / n;
+ for (let i = 0; i < n; i++) {
+ const u = (i + .5) / n, hh = h * (hk ? hk(u) : 1), dy = Math.sin(phase + u * freq * 6.2832) * amp + (dyk ? dyk(u) : 0);
+ ctx.drawImage(im, i * sw, 0, sw, im.height, -w / 2 + i * dw, -hh / 2 + dy, dw + .45, hh);
+ }
+ }
+ function drawFxProjectile(p) {
+ const im = images['fx-' + p.asset]; if (!im || p.wait > 0) return;
+ const age = p.age, size = p.size || 150, f = p.facing || 1, sp = Math.abs(p.vx || 0), gy = CONFIG.groundY;
+ const pop = easeOutBack(clamp(age / .18, 0, 1)), out = clamp(p.life / .25, 0, 1), stretch = 1 + Math.min(.2, sp / 4200);
+ ctx.save(); ctx.translate(Math.round(p.x), Math.round(p.y)); ctx.scale(f, 1); ctx.imageSmoothingEnabled = true;
+ const ghosts = (w, h, a0, dyo = 0) => { const n = p.tr.length; for (let j = 0; j < n; j++) { const t = p.tr[j], u = (j + 1) / (n + 1); ctx.globalAlpha = a0 * u * u * out; ctx.drawImage(im, (t.x - p.x) * f - w / 2, t.y - p.y - h / 2 + dyo, w, h); } ctx.globalAlpha = out; };
+ ctx.globalAlpha = out;
+ if (p.fx === 'rings') {
+ // Mega Chan: concentric arcs swell out of the wave front and pulse like a speaker; the red crescents ride in front.
+ const R = size * .6, c0 = -size * .3; ctx.lineCap = 'round';
+ for (let r = 0; r < 4; r++) {
+ const ph = (age * 1.6 + r / 4) % 1, rad = (.18 + ph * .95) * R * pop, pulse = 1 + .09 * Math.sin(age * 30 + r * 2), th = 1.0 - .3 * ph, al = Math.sin(Math.PI * Math.pow(ph, .5)) * out;
+ for (const [wd, col, a2] of [[(20 * (1 - ph) + 7) * pulse, '#e0301c', .8], [(9 * (1 - ph) + 3) * pulse, '#ff8a4a', .9], [(3.5 * (1 - ph) + 1.5) * pulse, '#fff4d0', 1]]) { ctx.globalAlpha = al * a2; ctx.strokeStyle = col; ctx.lineWidth = wd; ctx.beginPath(); ctx.arc(c0, 0, rad * pulse, -th, th); ctx.stroke(); }
+ }
+ ctx.globalAlpha = out * .95; const w = size * .78 * pop, hh = size * .6 * pop * (1 + .06 * Math.sin(age * 28)); ctx.drawImage(im, -w * .4, -hh / 2, w, hh);
+ } else if (p.fx === 'shout') {
+ ghosts(size * 1.1, size * .8, .35);
+ ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .5; ctx.drawImage(im, -size * .55 * 1.6 * pop, -size * .34 * pop, size * 1.1 * 1.6 * pop, size * .68 * pop);
+ ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = out; ctx.drawImage(im, -size * .5 * 1.45 * pop, -size * .3 * pop, size * 1.45 * pop, size * .6 * pop);
+ } else if (p.fx === 'chain') {
+ // Gufron Syududu: a chain of pulsing rings that weaves low through the air, each link beating a little after the one ahead.
+ const n = 6, sp0 = size * .19, amp = 30 * Math.min(1, age * 4);
+ for (let k = n - 1; k >= 0; k--) {
+ const beat = Math.max(0, Math.sin(age * 13 - k * 1.25)), sc = (1 - k * .085) * pop * (.82 + .3 * beat), wy = Math.sin(age * 8.5 - k * .85) * amp, tilt = Math.cos(age * 8.5 - k * .85) * .38;
+ ctx.save(); ctx.translate(-k * sp0, wy); ctx.rotate(tilt); ctx.globalAlpha = out * (1 - k * .12);
+ ctx.drawImage(im, -size * .46 * sc, -size * .46 * sc, size * .92 * sc, size * .92 * sc);
+ if (beat > .5) { ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .5 * (beat - .5) * 2; ctx.drawImage(im, -size * .46 * sc, -size * .46 * sc, size * .92 * sc, size * .92 * sc); }
+ ctx.restore();
+ }
+ } else if (p.fx === 'roll') {
+ const grow = fxGrow(p), w = size * 1.05 * stretch * pop, h = size * grow * pop, base = 101 - h * .195, roll = age * 11;
+ ghosts(w * .96, h * .9, .22, base);
+ ctx.translate(0, base);
+ fxStrips(im, w, h, 26, h * .022, 1.7, -roll, u => 1 + .34 * (u - .5) + .05 * Math.sin(roll + u * 9), u => -h * .09 * Math.max(0, u - .55) * 1.6 + Math.sin(roll * .7 + u * 3) * h * .012);
+ if (p.fire) { ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .35; fxStrips(im, w, h, 18, h * .022, 1.7, -roll, u => 1 + .34 * (u - .5)); }
+ } else if (p.fx === 'spray') {
+ const w = size * 1.1 * stretch * pop, h = size * .9 * pop;
+ ghosts(w * .95, h * .85, .25);
+ fxStrips(im, w, h, 32, h * .022, 2.2, -age * 20, u => .6 + .5 * u);
+ ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .22; ctx.drawImage(im, -w / 2, -h * .3, w, h * .6);
+ } else if (p.fx === 'flame') {
+ const w = size * 1.1 * stretch * pop, h = size * pop;
+ ghosts(w * .94, h * .9, .3);
+ fxStrips(im, w, h, 36, h * .02, 1.6, -age * 20, null);
+ ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .3; ctx.drawImage(im, -w / 2, -h / 2, w, h);
+ } else if (p.fx === 'drive') {
+ const w = size * stretch * pop, h = size * pop, bounce = Math.abs(Math.sin(age * 22)) * -4;
+ ghosts(w * .98, h, .18, bounce);
+ ctx.translate(0, gy - 6 - p.y - h * .15 + bounce + (p.y - (gy - 95)) * 0); ctx.drawImage(im, -w / 2, -h / 2, w, h);
+ } else if (p.fx === 'wave') {
+ const w = size * 1.12 * stretch * pop, h = size * pop;
+ ghosts(w * .96, h * .92, .24);
+ fxStrips(im, w, h, 22, h * .028, 1.6, -age * 13, null);
+ ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .28; ctx.drawImage(im, -w / 2, -h / 2, w, h);
+ } else if (p.fx === 'bull') { // banteng pantul: satu gambar utuh, ekor bayangan dan bara
+ const w = size * pop, h = size * pop;
+ ghosts(w * .96, h * .96, .32);
+ ctx.drawImage(im, -w / 2, -h / 2 + Math.sin(age * 40) * 2, w, h);
+ ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .22; ctx.drawImage(im, -w * .53, -h * .53, w * 1.06, h * 1.06);
+ } else if (p.fx === 'ant') { // semut: menghadap arah gerak, kaki bergetar; mengitari penyihir lalu mengejar
+ const ha = p.sw && !p.sw.go ? p.sw.hd : Math.atan2(p.vy || 0, p.vx || 1), left = Math.cos(ha) < 0, w = size * pop;
+ ctx.scale(f, 1); ctx.rotate(left ? ha - Math.PI : ha); if (left) ctx.scale(-1, 1);
+ ctx.drawImage(im, -w / 2, -w / 2 * (1 + .1 * Math.sin(age * 55 + (p.sw?.ang || 0))), w, w * (1 + .1 * Math.sin(age * 55 + (p.sw?.ang || 0))));
+ } else { // bolt: speech bubbles and other plain shots
+ const w = size * stretch * pop, h = size * pop;
+ ghosts(w * .95, h * .95, .28);
+ ctx.drawImage(im, -w / 2, -h / 2 + Math.sin(age * 18) * 2, w, h);
+ ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = out * .25; ctx.drawImage(im, -w * .55, -h * .55, w * 1.1, h * 1.1);
+ }
+ ctx.restore();
+ }
+ // ---- Mekanik jurus (flag data di manifest, dipakai pemain dan CPU lewat cfStrike) --------------------------------------
+ // rebound {range,damage,speed,asset} : dash menembus; banteng api terus melaju `range` px melewati lawan lalu memantul dan memukul lagi
+ // trail {gap,radius,dps,life} : gelombang meninggalkan genangan api di lantai (dps selama berdiri di dalamnya)
+ // split {at,dmg} : gelombang membelah jadi dua setelah menempuh `at` px (tiap belahan dmg x damage)
+ // dot {dps,time} : korosi, damage berkala setelah kena (diperbarui bila kena lagi)
+ // bind seconds : rantai membelit, lawan tertahan sejenak
+ // convoy {n,mul,gap} : n proyektil beruntun, masing-masing mul x damage
+ // aftershock {delay,radius,damage} : jurus area meledak lagi di titik yang sama dengan radius lebih besar
+ // pull px/s : jurus area menyedot lawan ke pusat selama aba-aba
+ // lift px/s : mengangkat lawan sedikit saat kena
+ // swarm {orbit,radius} + ramp {every,add}: semut mengitari penyihir dulu lalu mengejar satu per satu; damage naik di tengah
+ function fxTarget(owner) { return owner === 'enemy' ? hero : dummy; }
+ function fxLift(t, v) {
+ if (t === dummy) { if (dummy.state === 'down' || dummy.state === 'recover') return; dummy.vy = Math.min(dummy.vy, -v); dummy.y -= 2; }
+ else if (hero.hp > 0) { hero.vy = Math.min(hero.vy, -v); hero.y -= 2; hero.grounded = false; }
+ }
+ function fxBind(owner, secs, col) {
+ const t = fxTarget(owner); bwLock(owner === 'enemy' ? 'player' : 'enemy', secs);
+ effects.push({ type: 'cf-wrap', who: owner === 'enemy' ? 'hero' : 'dummy', life: secs, maxLife: secs, color: col, x: t.x, y: t.y });
+ }
+ function fxAddDot(owner, d, col) {
+ const old = cfx.dots.find(o => o.owner === owner);
+ if (old) { old.t = 0; old.dps = Math.max(old.dps, d.dps); return; }
+ cfx.dots.push({ owner, t: 0, time: d.time || 2.5, dps: d.dps || 3, acc: 0, shown: 0, color: d.color || col });
+ }
+ function fxFire(x, owner, o) {
+ cfx.hazards.push({ x, owner, t: 0, life: o.life || 3, radius: o.radius || 58, dps: o.dps || 3, img: null, fire: true, color: '#ff9a4a', acc: 0, shown: 0, seed: fxr() });
+ }
+ // Lidah api digambar sekali dalam ukuran satuan (lebar 1, tinggi 1) dengan gradasi yang dipakai ulang, lalu diskalakan.
+ let fxFlameG = null, fxGlowG = null;
+ function fxDrawFire(h) {
+ if (!fxFlameG) {
+ fxFlameG = ctx.createLinearGradient(0, 0, 0, -1); fxFlameG.addColorStop(0, '#ffe680'); fxFlameG.addColorStop(.35, '#ff9a2a'); fxFlameG.addColorStop(.8, 'rgba(232,72,28,.85)'); fxFlameG.addColorStop(1, 'rgba(232,72,28,0)');
+ fxGlowG = ctx.createRadialGradient(0, 0, 0, 0, 0, 1); fxGlowG.addColorStop(0, 'rgba(255,170,40,.95)'); fxGlowG.addColorStop(.6, 'rgba(255,90,20,.4)'); fxGlowG.addColorStop(1, 'rgba(255,60,10,0)');
+ }
+ const grow = Math.min(1, h.t / .25), fade = h.t > h.life - .6 ? Math.max(0, (h.life - h.t) / .6) : 1, gy = CONFIG.groundY + 6, R = h.radius;
+ ctx.save(); ctx.globalAlpha = fade * .55; ctx.translate(h.x, gy); ctx.save(); ctx.scale(R * 1.1, R * .3); ctx.fillStyle = fxGlowG; ctx.beginPath(); ctx.arc(0, 0, 1, 0, 7); ctx.fill(); ctx.restore();
+ ctx.globalAlpha = fade * .92; ctx.fillStyle = fxFlameG;
+ for (let i = 0; i < 8; i++) {
+ const u = i / 3.5 - 1, fh = (34 + 30 * (.5 + .5 * Math.sin(realTime * 9 + i * 1.9 + h.seed * 6))) * grow * (1 - Math.abs(u) * .4), sk = Math.sin(realTime * 7 + i * 2.3) * .45;
+ ctx.save(); ctx.translate(u * R * .85, 0); ctx.scale(13, fh);
+ ctx.beginPath(); ctx.moveTo(-1, 0); ctx.bezierCurveTo(-1.1, -.45, -.2, -.6, sk, -1); ctx.bezierCurveTo(.3, -.55, 1.1, -.4, 1, 0); ctx.closePath(); ctx.fill(); ctx.restore();
+ }
+ ctx.restore();
+ }
+ // Mekanik per proyektil; dipanggil dari fxProjTick sebelum tabrakan. 'skip' = tidak menabrak langkah ini, 'remove' = hapus.
+ function fxMech(p, dt) {
+ const home = p.owner === 'enemy' ? dummy : hero, tgt = fxTarget(p.owner);
+ if (p.ramp) p.damage = p.ramp.base + (p.grp.hits >= p.ramp.every ? p.ramp.add : 0);
+ if (p.sw && !p.sw.go) {
+ const s = p.sw; s.ang += s.w * dt;
+ const ex = s.R, ey = s.R * .62; p.x = home.x + Math.cos(s.ang) * ex; p.y = home.y - 100 + Math.sin(s.ang) * ey;
+ const vx = -Math.sin(s.ang) * ex * s.w, vy = Math.cos(s.ang) * ey * s.w; s.hd = Math.atan2(vy, vx); p.facing = vx >= 0 ? 1 : -1;
+ if (p.age < s.release) return 'skip';
+ s.go = true; const a = Math.atan2(tgt.y - 85 - p.y, tgt.x - p.x);
+ p.homing = true; p.speedH = s.speed; p.turn = s.turn; p.vx = Math.cos(a) * s.speed * .6; p.vy = Math.sin(a) * s.speed * .6; p.life = s.chase;
+ fxPart(p.x, p.y, 0, 0, .25, 5, '#3a2a1a', 'ember', {});
+ }
+ if (p.trail && p.age > .08 && Math.abs(p.x - (p.lastDrop ?? p.sx0)) >= (p.trail.gap || 110)) { p.lastDrop = p.x; fxFire(p.x, p.owner, p.trail); }
+ if (p.split && !p.splitDone && Math.abs(p.x - p.sx0) >= p.split.at) {
+ p.splitDone = true;
+ for (const s of [-1, 1]) projectiles.push({ ...p, tr: [], y: p.y + s * 14, vy: s * 90, damage: Math.max(1, Math.round(p.damage * (p.split.dmg || .55))), size: Math.round(p.size * .74), tall: 95, split: null, pass: true, spent: false, trT: 0 });
+ effects.push({ type: 'cf-ring', x: p.x, y: p.y, life: .3, maxLife: .3, color: p.color, r: 90 }); spawnParticles(p.x, p.y, p.color, 10, 220);
+ return 'remove';
+ }
+ if (p.reb) {
+ const R = p.reb;
+ if (!R.back && Math.abs(p.x - p.sx0) >= R.range) { R.back = true; p.spent = false; p.vx = -p.vx * 1.1; p.facing = p.vx >= 0 ? 1 : -1; effects.push({ type: 'cf-ring', x: p.x, y: p.y, life: .3, maxLife: .3, color: p.color, r: 80 }); spawnParticles(p.x, p.y, p.color, 12, 260); sound('hit'); }
+ if (R.back && Math.abs(home.x - p.x) < 50) return 'remove';
+ }
+ return null;
+ }
+ function fxOnHit(p) {
+ const t = fxTarget(p.owner), f = p.facing || 1, x = t.x - f * 12, y = t.y - 90, col = p.color || '#fff';
+ const landed = t.state === 'hurt' || t.state === 'down'; // hit yang tertahan blok/parry tidak memicu efek susulan
+ if (landed) {
+ if (p.grp) p.grp.hits++;
+ if (p.lift) fxLift(t, p.lift);
+ if (p.dot) fxAddDot(p.owner, p.dot, col);
+ if (p.bind) fxBind(p.owner, p.bind, col);
+ }
+ if (p.fx === 'roll') for (let i = 0; i < 16; i++) fxPart(x, t.y - 20 - fxr() * 40, (fxr() - .5) * 300, -260 - fxr() * 340, .6, 4 + fxr() * 6, p.fire ? '#ffb347' : '#e8fff6', p.fire ? 'ember' : 'foam', { g: 900, drag: 1 });
+ else if (p.fx === 'shout') { for (let i = 0; i < 9; i++) fxPart(x, y + (fxr() - .5) * 60, (fxr() - .5) * 420, -120 - fxr() * 240, .6, 16 + fxr() * 12, '#d8f1ff', 'glyph', { ch: FX_GLYPHS[Math.floor(fxr() * FX_GLYPHS.length)], g: 500, drag: 1.5 }); effects.push({ type: 'cf-ring', x, y, life: .3, maxLife: .3, color: '#8fd0ff', r: 70 }); }
+ else if (p.fx === 'rings') effects.push({ type: 'cf-ring', x, y, life: .4, maxLife: .4, color: '#ff5a3a', r: 100 });
+ else if (p.fx === 'chain') { for (let i = 0; i < 6; i++) fxPart(x, y + (fxr() - .5) * 60, (fxr() - .5) * 220, -80 - fxr() * 160, .7, 14 + fxr() * 8, '#ffe9a6', 'glyph', { ch: fxr() < .5 ? '♪' : '♫', g: -40, drag: 1.5, edge: '#5a3a08' }); effects.push({ type: 'cf-ring', x, y, life: .35, maxLife: .35, color: '#ffd24a', r: 80 }); }
+ else if (p.fx === 'spray') for (let i = 0; i < 14; i++) fxPart(x, y + (fxr() - .5) * 70, (fxr() - .3) * 300 * f, -200 * fxr(), .6, 4 + fxr() * 5, fxr() < .5 ? '#a6f04a' : '#d8ff7a', 'foam', { g: 700, drag: 1.2 });
+ else if (p.fx === 'ant') for (let i = 0; i < 4; i++) fxPart(x, y, (fxr() - .5) * 200, -80 - fxr() * 120, .35, 3, '#2a1a10', 'chip', { g: 800 });
+ else if (p.fx === 'drive') { dust(x, 8); for (let i = 0; i < 6; i++) fxPart(x, t.y - 10, (fxr() - .5) * 300, -200 - fxr() * 200, .5, 4, '#c9b48a', 'chip', { g: 1400, drag: .6 }); }
+ }
+ // Jurus area: cincin aba-aba (0,25 s sebelum kena, mengikuti timer aksi), lalu gelombang kejut, retakan dan serpihan.
+ const FX_TELE = .25;
+ function fxAreaBlast(x, R, col, big) {
+ const gy = CONFIG.groundY;
+ effects.push({ type: 'cf-shock', x, y: gy, life: big ? .6 : .5, maxLife: big ? .6 : .5, r: R, color: col });
+ const segs = [], n = 7 + Math.round(R / 30);
+ for (let i = 0; i < n; i++) { const a = fxr() * 6.2832, len = R * (.45 + fxr() * .85), pts = [[0, 0]]; for (let j = 1; j <= 4; j++) { const u = j / 4, jit = (fxr() - .5) * 16; pts.push([Math.cos(a) * len * u + jit, Math.sin(a) * len * u * .24 + 7 + Math.abs(jit) * .25]); } segs.push(pts); }
+ effects.push({ type: 'cf-crack', x, y: gy, life: 1.5, maxLife: 1.5, segs });
+ for (let i = 0; i < 12; i++) fxPart(x + (fxr() - .5) * R * .7, gy - 6, (fxr() - .5) * 360, -260 - fxr() * 300, .5 + fxr() * .3, 3 + fxr() * 4, fxr() < .5 ? '#8a6a45' : '#cdb78e', 'chip', { g: 1500, drag: .5 });
+ }
+ function fxAreaTick(a, dt) {
+ const act = a.action; if (!act || act.kind !== 'area' || act.fired || !act.pull) return;
+ const hx = a.x + a.facing * (act.ahead || 0), tg = a === hero ? dummy : hero;
+ if (!bwAlive(tg) || tg.y < CONFIG.groundY - 50 || Math.abs(tg.x - hx) > (act.radius || 100) * 2.6) return;
+ const dx = hx - tg.x; tg.x = clamp(tg.x + Math.sign(dx) * Math.min(Math.abs(dx), act.pull * dt), tg === hero ? 70 : 110, tg === hero ? W - 70 : W - 100);
+ }
+ // Kemajuan aba-aba jurus area milik `a` (0..1), atau -1 bila tidak sedang aba-aba.
+ function fxTeleP(a) {
+ const act = a.action; if (!act || act.kind !== 'area' || act.fired) return -1;
+ const hitT = act.duration * (act.hitAt ?? .5); return act.t < hitT - FX_TELE ? -1 : clamp((act.t - (hitT - FX_TELE)) / FX_TELE, 0, 1);
+ }
+ function fxDrawTele(a, col) {
+ const act = a.action, p = fxTeleP(a); if (p < 0) return;
+ const hx = a.x + a.facing * (act.ahead || 0), gy = CONFIG.groundY + 4, R = act.radius || 100, Rr = R * (1.55 - .55 * p);
+ ctx.save(); ctx.lineCap = 'round';
+ ctx.globalAlpha = .12 + .22 * p; ctx.fillStyle = col; ctx.beginPath(); ctx.ellipse(hx, gy, R * p, R * p * .26, 0, 0, 7); ctx.fill();
+ ctx.globalAlpha = .35 + .6 * p; ctx.strokeStyle = col; ctx.lineWidth = 3 + 3 * p; ctx.beginPath(); ctx.ellipse(hx, gy, Rr, Rr * .26, 0, 0, 7); ctx.stroke();
+ ctx.globalAlpha = .5 * p; ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.ellipse(hx, gy, R, R * .26, 0, 0, 7); ctx.stroke();
+ if (act.pull) for (let j = 0; j < 6; j++) { const ang = realTime * 7 + j * 1.047, q = (realTime * 1.6 + j / 6) % 1, rr = R * 2.2 * (1 - q); ctx.globalAlpha = .6 * (1 - q); ctx.fillStyle = col; ctx.beginPath(); ctx.arc(hx + Math.cos(ang) * rr, gy - 6 + Math.sin(ang) * rr * .26 - (1 - q) * 20, 4, 0, 7); ctx.fill(); }
+ ctx.restore();
+ }
+ // Dash: bayangan sprite yang memudar, debu di tanah dan garis kecepatan.
+ const fxPool = [];
+ function fxCv(w, h) { const c = fxPool.pop() || document.createElement('canvas'); c.width = w; c.height = h; return c; }
+ function fxGhost(a, col) {
+ let img, r, ax, ay, sx, sy;
+ if (a === hero) {
+ if (!images.hero || !manifest) return null;
+ let list = frames(hero.state); if (!list.length) list = frames('idle'); if (!list.length) return null;
+ const pose = heroPose(); r = list[pose.frame]; if (!r) return null;
+ const cw = manifest.cell?.width || manifest.cell?.w || r.w, ch = manifest.cell?.height || manifest.cell?.h || r.h, sc = pose.scale * stateK(metrics, hero.state);
+ img = images.hero; ax = manifest.cell?.anchor_x ?? cw / 2; ay = manifest.cell?.anchor_y ?? ch - (manifest.cell?.safe_margin_y || 0); sx = hero.facing * sc; sy = sc;
+ } else {
+ const pose = opponentPose(), m = pose.manifest, fm = CF[opponentCharacter]; if (!m || !fm) return null;
+ img = images[cfFormOn(opponentCharacter, 'enemy') ? opponentCharacter + '-form' : opponentCharacter]; r = m.frame_layout?.rows?.[pose.state]?.[pose.frame]; if (!img || !r) return null;
+ const k = stateK(cfFormOn(opponentCharacter, 'enemy') ? fm.mode?.form?.metrics : fm.metrics, pose.state);
+ ax = m.cell.width / 2; ay = m.cell.height - m.cell.safe_margin_y; sx = (dummy.facing || -1) * k; sy = k;
+ }
+ const cv = fxCv(r.w, r.h), c2 = cv.getContext('2d');
+ c2.drawImage(img, r.x, r.y, r.w, r.h, 0, 0, r.w, r.h); c2.globalCompositeOperation = 'source-atop'; c2.globalAlpha = .7; c2.fillStyle = col; c2.fillRect(0, 0, r.w, r.h);
+ return { cv, ax, ay, sx, sy, x: a.x, y: a.y, life: .24, max: .24 };
+ }
+ function fxDashTick(a, dt) {
+ const act = a.action; if (!act || act.kind !== 'dash' || Math.abs(a.vx) < 300 || act.t > act.duration * .72) return;
+ act.fxT = (act.fxT || 0) - dt; if (act.fxT > 0) return; act.fxT = .03;
+ const col = (a === hero ? CF[selectedCharacter] : CF[opponentCharacter])?.color || '#ffd36a', f = a.facing || 1, g = fxGhost(a, col);
+ if (g) { cfx.ghosts.push(g); if (cfx.ghosts.length > 12) fxPool.push(cfx.ghosts.shift().cv); }
+ spawnParticles(a.x - f * 24, CONFIG.groundY - 5, '#d6c6a3', 2, 70, 'dust');
+ for (let i = 0; i < 2; i++) fxPart(a.x - f * (40 + fxr() * 30), a.y - 15 - fxr() * 140, -f * (260 + fxr() * 200), 0, .16 + fxr() * .08, 2 + fxr() * 1.5, i ? '#ffffff' : col, 'streak', { len: 60 + fxr() * 70, g: 0, drag: .3 });
+ }
+ // Selama dash, gambar jurusnya (banteng, mobil) ikut melaju di depan penyerang, di belakang sprite.
+ function fxDrawDash(a, def) {
+ const act = a.action; if (!def || !act || act.kind !== 'dash' || act.fired || act.t < act.duration * .12) return;
+ const im = images['fx-' + def.id + '-' + act.asset]; if (!im) return;
+ const k = clamp((act.t - act.duration * .12) / (act.duration * .2), 0, 1), size = (act.size || 220) * (.62 + .38 * easeOutBack(k));
+ ctx.save(); ctx.translate(Math.round(a.x + a.facing * 50), Math.round(a.y - 82)); ctx.scale(a.facing, 1); ctx.globalAlpha = .92 * k; ctx.imageSmoothingEnabled = true; ctx.drawImage(im, -size / 2, -size / 2, size, size); ctx.restore();
+ }
+ function fxDrawGhosts() {
+ for (const g of cfx.ghosts) { const k = g.life / g.max; ctx.save(); ctx.globalAlpha = k * .55; ctx.translate(Math.round(g.x), Math.round(g.y)); ctx.scale(g.sx, g.sy); ctx.drawImage(g.cv, -g.ax, -g.ay); ctx.restore(); }
+ }
+ // Satu langkah untuk semua pengatur waktu FX; dipanggil dari updateCf.
+ function fxTick(dt) {
+ for (const a of [hero, dummy]) { fxDashTick(a, dt); fxAreaTick(a, dt); }
+ for (let n = cfx.ghosts.length - 1; n >= 0; n--) { const g = cfx.ghosts[n]; g.life -= dt; if (g.life <= 0) { fxPool.push(g.cv); cfx.ghosts.splice(n, 1); } }
+ for (let n = cfx.later.length - 1; n >= 0; n--) { const l = cfx.later[n]; l.t -= dt; if (l.t <= 0) { cfx.later.splice(n, 1); l.fn(); } }
+ for (let n = cfx.dots.length - 1; n >= 0; n--) {
+ const d = cfx.dots[n], t = fxTarget(d.owner); d.t += dt;
+ if (d.t > d.time || !bwAlive(t)) { cfx.dots.splice(n, 1); continue; }
+ d.acc += d.dps * dt;
+ if (d.acc >= 1) { const m = Math.floor(d.acc); d.acc -= m; d.shown += m; dotDamage(t, m, d.color, false); }
+ if (d.shown >= 3) { damageNumbers.push({ x: t.x + (fxr() - .5) * 24, y: t.y - 130, amount: d.shown, life: .9, maxLife: .9, color: d.color }); d.shown = 0; }
+ if (fxr() < dt * 16) fxPart(t.x + (fxr() - .5) * 56, t.y - 20 - fxr() * 110, 0, -50 - fxr() * 60, .8, 4 + fxr() * 4, d.color, 'bubble', { g: -30, drag: 1 });
+ }
+ for (const h of cfx.hazards) if (h.fire && fxr() < dt * 9) fxPart(h.x + (fxr() - .5) * h.radius * 1.4, CONFIG.groundY - 8, (fxr() - .5) * 20, -60 - fxr() * 90, .7, 3 + fxr() * 3, '#ffb347', 'ember', { g: -30, drag: 1 });
+ }
// ---- BRO WO status layer: cinematic camera, stun holds, poison drain, rage -------------------------------------------
// `bwx` is shared by the player's and the CPU's BRO WO; 'player' is the hero, 'enemy' is the dummy.
const bwx = { zoom: 1, zoomT: 1, dim: 0, dimT: 0, cx: 640, cy: 480, rage: { player: 0, enemy: 0 }, poison: { player: null, enemy: null }, lock: { player: 0, enemy: 0 }, dance: [], giant: null, fires: [], burn: { player: 0, enemy: 0, shown: { player: 0, enemy: 0 } }, tornado: null };
function bwReset() {
bwx.zoom = bwx.zoomT = 1; bwx.dim = bwx.dimT = 0; bwx.rage.player = bwx.rage.enemy = 0; bwx.poison.player = bwx.poison.enemy = null; bwx.lock.player = bwx.lock.enemy = 0; bwx.dance.length = 0; bwx.giant = null;
- bwx.fires.length = 0; bwx.burn.player = bwx.burn.enemy = 0; bwx.burn.shown.player = bwx.burn.shown.enemy = 0; bwx.tornado = null; cfx.ents.length = cfx.hazards.length = cfx.marks.length = 0;
+ bwx.fires.length = 0; bwx.burn.player = bwx.burn.enemy = 0; bwx.burn.shown.player = bwx.burn.shown.enemy = 0; bwx.tornado = null; cfx.ents.length = cfx.hazards.length = cfx.marks.length = cfx.later.length = cfx.dots.length = cfx.ghosts.length = 0;
hero.bwRage = dummy.bwRage = false; hero.bwSaved = dummy.bwSaved = null;
{ const b = bwBaseCooldowns(selectedCharacter); if (b) Object.assign(cooldownMax, b); if (CF[selectedCharacter]?.mode?.form) syncPlayerForm(); }
}
@@ -1386,12 +1728,14 @@
}
function updateProjectiles(dt) {
for (let i = projectiles.length - 1; i >= 0; i--) { const p = projectiles[i], oldX = p.x;
+ if (p.wait > 0) { p.wait -= dt; continue; } // konvoi: menunggu giliran
// NIB paper planes steer toward the rival's chest, turning at most p.turn rad/s.
if (p.homing) { const t = p.owner === 'enemy' ? hero : dummy, want = Math.atan2(t.y - 85 - p.y, t.x - p.x), cur = Math.atan2(p.vy, p.vx); let d = want - cur; d = Math.atan2(Math.sin(d), Math.cos(d)); const ang = cur + clamp(d, -p.turn * dt, p.turn * dt); p.vx = Math.cos(ang) * p.speedH; p.vy = Math.sin(ang) * p.speedH; p.facing = p.vx >= 0 ? 1 : -1; }
// EDDA Stone Skip: the stone falls under its own gravity and hops off the floor p.bounces more times, then sinks.
if (p.skip) { p.vy += p.skipG * dt; if (p.vy > 0 && p.y + p.vy * dt >= CONFIG.groundY - 14) { effects.push({ type: 'edda-fx', asset: 'edda-ripple', x: p.x, y: CONFIG.groundY - 14, facing: p.facing, life: .3, maxLife: .3, size: 110 }); if (p.bounces-- > 0) { p.y = CONFIG.groundY - 14; p.vy = -p.hop; sound('step'); } else { projectiles.splice(i, 1); continue; } } }
if (p.cfLand) { p.vy += p.cfG * dt; if (p.vy > 0 && p.y + p.vy * dt >= CONFIG.groundY - 8) { cfLand(p); projectiles.splice(i, 1); continue; } }
if (p.gravity) p.vy += p.gravity * dt; p.x += p.vx * dt; p.y += (p.vy || 0) * dt; p.life -= dt;
+ if (p.fx) { const r = fxProjTick(p, dt); if (r === 'remove') { projectiles.splice(i, 1); continue; } if (r === 'skip') continue; }
if (p.gravity && p.y >= CONFIG.groundY - 8) { slagSplash(p); projectiles.splice(i, 1); continue; }
if (p.landY && p.y >= p.landY) { lanceShatter(p); projectiles.splice(i, 1); continue; }
// RHEA: a Gravity Well waits in place and collapses; an Orrery planet laps around its owner.
@@ -1409,11 +1753,11 @@
}
if (p.spent) { if (p.life <= 0) projectiles.splice(i, 1); continue; }
if(p.owner==='enemy') {
- if(Math.min(oldX,p.x)<=hero.x+28&&Math.max(oldX,p.x)>=hero.x-28&&Math.abs(p.y-(hero.y-85))<(p.tall||75)&&window.__game.receiveHit(p.damage,{projectile:true,freeze:p.freeze}))p.boomerang?(p.spent=true,p.returning=true):(p.orbit||p.pass)?(p.spent=true):p.life=0;
+ if(Math.min(oldX,p.x)<=hero.x+28&&Math.max(oldX,p.x)>=hero.x-28&&Math.abs(p.y-(hero.y-85))<(p.tall||75)&&window.__game.receiveHit(p.damage,{projectile:true,freeze:p.freeze})){if(p.fx)fxOnHit(p);p.boomerang?(p.spent=true,p.returning=true):(p.orbit||p.pass)?(p.spent=true):p.life=0;}
if(p.life<=0||p.x<-100||p.x>W+100)projectiles.splice(i,1);continue;
}
// Swept x interval keeps a high-speed bolt from tunneling through a target.
- if (Math.min(oldX, p.x) <= dummy.x + 30 && Math.max(oldX, p.x) >= dummy.x - 30 && Math.abs(p.y - (dummy.y - (F?95:68))) < (p.tall || (F?90:66)) && dummy.state !== 'down' && dummy.state !== 'recover') { if (hitDummy(p.damage||BALANCE.skill1.damage, p.knockback ?? BALANCE.skill1.knockback, p.color || '#94ffde', oldX, p.y, !!p.knockdown, false)) { if (p.boomerang) { p.spent = true; p.returning = true; } else if (p.orbit || p.pass) p.spent = true; else p.life = 0; } }
+ if (Math.min(oldX, p.x) <= dummy.x + 30 && Math.max(oldX, p.x) >= dummy.x - 30 && Math.abs(p.y - (dummy.y - (F?95:68))) < (p.tall || (F?90:66)) && dummy.state !== 'down' && dummy.state !== 'recover') { if (hitDummy(p.damage||BALANCE.skill1.damage, p.knockback ?? BALANCE.skill1.knockback, p.color || '#94ffde', oldX, p.y, !!p.knockdown, false)) { if (p.fx) fxOnHit(p); if (p.boomerang) { p.spent = true; p.returning = true; } else if (p.orbit || p.pass) p.spent = true; else p.life = 0; } }
if (p.life <= 0 || p.x < -100 || p.x > W + 100) projectiles.splice(i, 1);
}
}
@@ -1770,7 +2114,7 @@
function cpuReach(m) { return m.projectile ? (m.range || 560) : (m.area ? m.reach : m.reach + 27) + (m.dash ? m.dash * m.duration * .4 : 0); }
// Open a chain only from where every link of it still reaches (knockback slides the player back a little).
function cpuChainReach(P) { let r = Infinity; for (let i = 1; i <= Math.max(1, P.combo); i++) r = Math.min(r, cpuReach(cpuMove('attack', i))); return Math.max(60, r - 14); }
- function cpuHitTime(m) { return m.duration * (m.hitAt || .5) + (m.projectile ? Math.max(0, Math.abs(hero.x - dummy.x) - 100) / (m.speed || 800) : 0); }
+ function cpuHitTime(m) { return m.duration * (m.hitAt || .5) + (m.projectile ? Math.max(0, Math.abs(hero.x - dummy.x) - 100) / (m.speed || 800) + (m.swarm?.orbit || 0) : 0); }
// True when the hit would land on a vulnerable player: inside the current hurt window, after post-hurt immunity,
// or after a skill/ultimate the player is immune during. A fresh engagement never starts on a stunned player:
// only the confirmed chain (combo + one ender) may use the stun, so there are no infinite loops.
@@ -1978,7 +2322,7 @@
return {state:name,frame:clamp(index,0,Math.max(0,list.length-1)),manifest:m};
}
function updateVisuals(dt) {
- for (let i = particles.length - 1; i >= 0; i--) { const p = particles[i]; p.life -= dt; p.x += p.vx * dt; p.y += p.vy * dt; p.vy += (p.kind === 'dust' || p.kind==='run-smoke' ? -40 : 680) * dt; p.vx *= Math.exp(-dt * 3); if (p.life <= 0) particles.splice(i, 1); }
+ for (let i = particles.length - 1; i >= 0; i--) { const p = particles[i]; p.life -= dt; p.x += p.vx * dt; p.y += p.vy * dt; p.vy += (p.g ?? (p.kind === 'dust' || p.kind==='run-smoke' ? -40 : 680)) * dt; p.vx *= Math.exp(-dt * (p.drag ?? 3)); if (p.life <= 0) particles.splice(i, 1); }
for (let i = effects.length - 1; i >= 0; i--) { effects[i].life -= dt; if (effects[i].life <= 0) effects.splice(i, 1); }
for (let i = damageNumbers.length - 1; i >= 0; i--) { damageNumbers[i].life -= dt; damageNumbers[i].y -= dt * 55; if (damageNumbers[i].life <= 0) damageNumbers.splice(i, 1); }
}
@@ -2116,6 +2460,7 @@
}
function drawEffects() {
for (const p of projectiles) {
+ if (p.fx && images['fx-' + p.asset]) { drawFxProjectile(p); continue; }
if(p.asset && images['fx-'+p.asset]){const w=p.size||84,h=p.size||76;ctx.save();ctx.translate(p.x,p.y);ctx.scale(p.facing,1);if(p.vy)ctx.rotate(Math.atan2(p.vy,Math.abs(p.vx)));if(p.spin)ctx.rotate(realTime*p.spin);ctx.drawImage(images['fx-'+p.asset],-w/2,-h/2,w,h);ctx.restore();continue;}
ctx.save(); ctx.translate(Math.round(p.x), Math.round(p.y)); ctx.scale(p.facing, 1);
const glow = ctx.createLinearGradient(-75, 0, 15, 0); glow.addColorStop(0, '#73f2dd00'); glow.addColorStop(1, '#affff1bb'); ctx.fillStyle = glow; ctx.beginPath(); ctx.moveTo(-80, -5); ctx.lineTo(10, -11); ctx.lineTo(25, 0); ctx.lineTo(10, 11); ctx.lineTo(-80, 5); ctx.fill();
@@ -2133,6 +2478,15 @@
if (e.type === 'impact' || e.type === 'muzzle') { ctx.rotate(t * .4); ctx.fillStyle = e.color || '#b2ffef'; for (let i = 0; i < 8; i++) { ctx.rotate(Math.PI / 4); ctx.beginPath(); ctx.moveTo(5, -3); ctx.lineTo((e.heavy ? 60 : 40) * (1 - t) + 7, 0); ctx.lineTo(5, 4); ctx.fill(); } ctx.fillStyle = '#fff9e5'; ctx.fillRect(-7, -7, 14, 14); }
if (e.type === 'slam') { ctx.strokeStyle = '#ccf0ff'; ctx.lineWidth = 8 * (1 - t) + 1; ctx.beginPath(); ctx.ellipse(0, -2, 35 + t * 240, 10 + t * 35, 0, 0, Math.PI * 2); ctx.stroke(); for (let i = -3; i <= 3; i++) { const height = (95 - Math.abs(i) * 12) * Math.sin(Math.PI * Math.min(t * 1.4, 1)); ctx.fillStyle = i % 2 ? '#76b1c9dd' : '#d7eff2cc'; ctx.beginPath(); ctx.moveTo(i * 48 - 15, 0); ctx.lineTo(i * 48 - 8, -height); ctx.lineTo(i * 48 + 16, -height * .6); ctx.lineTo(i * 48 + 21, 0); ctx.closePath(); ctx.fill(); } }
if (e.type === 'charge' || e.type === 'ultimate-ring') { ctx.strokeStyle = '#e7bb79'; ctx.lineWidth = 3; const radius = e.type === 'charge' ? 100 - t * 65 : 70 + t * 150; ctx.rotate(realTime * 2); ctx.strokeRect(-radius * .7, -radius * .7, radius * 1.4, radius * 1.4); ctx.rotate(Math.PI / 4); ctx.strokeRect(-radius * .6, -radius * .6, radius * 1.2, radius * 1.2); ctx.beginPath(); ctx.arc(0, 0, radius, 0, Math.PI * 2); ctx.stroke(); }
+ if (e.type === 'cf-wrap') { // rantai membelit: tiga elips emas berputar di sekeliling lawan (mengikuti posisinya)
+ const t = e.who === 'hero' ? hero : dummy; ctx.translate(t.x - e.x, t.y - 85 - e.y); ctx.globalAlpha = Math.min(1, e.life / e.maxLife * 3); ctx.lineCap = 'round';
+ for (let j = 0; j < 3; j++) for (const [col, wd] of [['#3a2406', 8 - j], [e.color, 4 - j * .6]]) { ctx.strokeStyle = col; ctx.lineWidth = wd; ctx.beginPath(); ctx.ellipse(0, (j - 1) * 36, 54 - j * 4, 14, realTime * 5 * (j % 2 ? -1 : 1) + j, 0, 5.2); ctx.stroke(); }
+ }
+ if (e.type === 'cf-ring') { const rr = e.r * (.25 + .85 * t); ctx.globalAlpha = (1 - t) * .9; ctx.strokeStyle = e.color; ctx.lineWidth = 9 * (1 - t) + 1.5; ctx.beginPath(); ctx.arc(0, 0, rr, 0, 7); ctx.stroke(); ctx.globalAlpha = (1 - t) * .7; ctx.strokeStyle = '#fff'; ctx.lineWidth = 2.5 * (1 - t) + .5; ctx.beginPath(); ctx.arc(0, 0, rr * .82, 0, 7); ctx.stroke(); }
+ if (e.type === 'cf-shock') { // gelombang kejut di lantai: dua cincin elips melebar + cahaya di tengah
+ for (let j = 0; j < 2; j++) { const tt = clamp(t * (1.15 - j * .2) - j * .06, 0, 1), rr = e.r * (.2 + 1.15 * tt); ctx.globalAlpha = (1 - tt) * (j ? .6 : .95); ctx.strokeStyle = j ? '#fff' : e.color; ctx.lineWidth = (j ? 4 : 11) * (1 - tt) + 1.5; ctx.beginPath(); ctx.ellipse(0, 4, rr, rr * .26, 0, 0, 7); ctx.stroke(); }
+ ctx.globalAlpha = (1 - t) * (1 - t) * .55; const gl = ctx.createRadialGradient(0, -4, 2, 0, -4, e.r * .8); gl.addColorStop(0, '#fff'); gl.addColorStop(.4, e.color); gl.addColorStop(1, 'rgba(255,255,255,0)'); ctx.fillStyle = gl; ctx.beginPath(); ctx.ellipse(0, -4, e.r * .8, e.r * .26, 0, 0, 7); ctx.fill();
+ }
if (e.type === 'drone-laser') {
const dx = e.endX - e.x, dy = e.endY - e.y, length = Math.hypot(dx, dy);
ctx.rotate(Math.atan2(dy, dx)); ctx.lineCap = 'round';
@@ -2145,13 +2499,14 @@
ctx.restore();
}
for (const p of particles) {
- ctx.save(); ctx.globalAlpha=clamp(p.life/.3,0,1); ctx.fillStyle=p.color; const size=Math.max(1,Math.round(p.size));
+ ctx.save(); ctx.globalAlpha=clamp(p.life/(p.fade?p.maxLife:.3),0,1)*(p.alpha??1); ctx.fillStyle=p.color; const size=Math.max(1,Math.round(p.size));
if(p.kind==='run-smoke') {
const age=1-p.life/p.maxLife, radius=p.size+age*13, x=Math.round(p.x), y=Math.round(p.y);
ctx.translate(x,y); ctx.scale(1,.68);
const fog=ctx.createRadialGradient(0,0,0,0,0,radius); fog.addColorStop(0,'#edf0e0eb'); fog.addColorStop(.45,'#cbd2c2cc'); fog.addColorStop(1,'#b4c0ac00');
ctx.globalAlpha*=.9; ctx.fillStyle=fog; ctx.beginPath(); ctx.arc(0,0,radius,0,Math.PI*2); ctx.fill();
- } else if(p.kind==='dust') {ctx.globalAlpha*=.35;ctx.fillRect(Math.round(p.x),Math.round(p.y),size*3,size*2);}
+ } else if(p.fade) fxDrawPart(p);
+ else if(p.kind==='dust') {ctx.globalAlpha*=.35;ctx.fillRect(Math.round(p.x),Math.round(p.y),size*3,size*2);}
else ctx.fillRect(Math.round(p.x),Math.round(p.y),size,size);
ctx.restore();
}
@@ -2383,6 +2738,8 @@
setPaused, setHitboxes(v) { hitboxes = !!v; $('#hitbox-toggle').checked = hitboxes; },
receiveHit(damage = 12,options={}) { if (menuOpen || (match&&match.phase!=='fight') || hero.hp<=0 || hero.invuln > 0) return false; if (hero.action?.guard && shellGuard(hero)) return false; if (heroGuard(damage, options)) return true; if (hero.action?.type?.startsWith('skill') || hero.action?.type === 'ultimate') return false; hero.hp = Math.max(0, hero.hp - damage); hero.action = null; hero.rollBuffer = null; hero.doublePose = 0; hero.hurtTime = hero.hp>0?.42:0; hero.vx = hero.facing * -170; state(hero.hp>0?'hurt':'down'); spawnParticles(hero.x, hero.y - 75, '#ffc694', 14, 200); if(options.freeze!==false)hitstop = .05; trauma = Math.min(1, trauma + .2 * (options.shake ?? 1)); if (!hero.hp) {hero.koTime=1.7;clearInput();if(match?.mode!=='versus')announce('CORE RESTART',1.7);} return true; }
};
+ // Pemeriksaan efek jurus untuk uji headless (tests/t1-fx.mjs).
+ window.__game.fx = { snapshot: () => ({ ghosts: cfx.ghosts.length, hazards: cfx.hazards.filter(h => h.fire).length, dots: cfx.dots.length, later: cfx.later.length, particles: particles.length, effects: effects.map(e => e.type), tele: { hero: fxTeleP(hero), dummy: fxTeleP(dummy) }, projectiles: projectiles.map(p => ({ fx: p.fx, x: Math.round(p.x), y: Math.round(p.y), age: +(p.age || 0).toFixed(3), damage: p.damage, wait: p.wait || 0, orbit: !!(p.sw && !p.sw.go) })) }) };
if(F)reset();
if(window.FRONTEND_ENABLED)setMenuOpen(true);
resize(); requestAnimationFrame(tick);
diff --git a/tests/t1-fx.mjs b/tests/t1-fx.mjs
new file mode 100644
index 0000000..f0e5683
--- /dev/null
+++ b/tests/t1-fx.mjs
@@ -0,0 +1,149 @@
+// Tahap 1.1-1.6: efek jurus (gelombang, area, dash, jurus mode, semut Gufron).
+// node tests/harness.mjs tests/t1-fx.mjs
+// Setiap jurus (skill1, skill2, versi mode) enam karakter berbasis data dilepas dari sisi pemain DAN sisi lawan (P2/CPU memakai
+// data jurus yang sama). Menguji: damage tetap dalam batas, waktu kena tidak bergeser, efek benar-benar bergerak, mekanik jurus
+// mode (rebound, trail, split, dot, bind, convoy, aftershock, pull), dan mengambil tangkapan layar ke FX_SHOTS (default
+// /t1-fx-shots) untuk ditinjau mata. Gagal bila ada pernyataan yang salah atau galat halaman.
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+
+export const options = { viewport: { width: 1280, height: 720 } };
+const OUT = process.env.FX_SHOTS || path.join(os.tmpdir(), 't1-fx-shots');
+const DT = 1 / 60;
+
+// Daftar jurus: id, skill, mode?, jarak target (D) agar kena, kind, dan waktu tangkapan layar setelah dilepas.
+const MOVES = [
+ { c: 'megachan', s: 'skill1', k: 'dash', D: 300, T: [.15, .25, .36, .55] },
+ { c: 'megachan', s: 'skill2', k: 'wave', D: 520, T: [.4, .5, .62, .8], base: 16 },
+ { c: 'megachan', s: 'skill1', m: 1, k: 'rebound', D: 300, T: [.3, .5, .8, 1.1] },
+ { c: 'megachan', s: 'skill2', m: 1, k: 'trail', D: 520, T: [.45, .6, .9, 1.6] },
+ { c: 'sonofsolo', s: 'skill1', k: 'lob', D: 450, T: [.4, .7, 1.0, 1.4] },
+ { c: 'sonofsolo', s: 'skill2', k: 'wave', D: 520, T: [.4, .5, .62, .8], base: 15 },
+ { c: 'sonofsolo', s: 'skill1', m: 1, k: 'dot', D: 480, T: [.4, .55, .8, 1.5] },
+ { c: 'sonofsolo', s: 'skill2', m: 1, k: 'lob', D: 450, T: [.4, .7, 1.0, 1.6] },
+ { c: 'ethanolman', s: 'skill1', k: 'wave', D: 480, T: [.4, .5, .62, .8], base: 17, lift: true },
+ { c: 'ethanolman', s: 'skill2', k: 'area', D: 100, T: [.15, .3, .4, .7] },
+ { c: 'ethanolman', s: 'skill1', m: 1, k: 'trail', D: 500, T: [.45, .6, .9, 1.6] },
+ { c: 'ethanolman', s: 'skill2', m: 1, k: 'aftershock', D: 100, T: [.15, .32, .45, .7] },
+ { c: 'anice', s: 'skill1', k: 'wave', D: 520, T: [.4, .5, .62, .8], base: 14 },
+ { c: 'anice', s: 'skill2', k: 'area', D: 110, T: [.15, .34, .45, .8] },
+ { c: 'anice', s: 'skill1', m: 1, k: 'dash', D: 300, T: [.15, .25, .36, .55] },
+ { c: 'anice', s: 'skill2', m: 1, k: 'split', D: 560, T: [.45, .65, .85, 1.1] },
+ { c: 'soloman', s: 'skill1', k: 'dash', D: 300, T: [.15, .25, .36, .55] },
+ { c: 'soloman', s: 'skill2', k: 'area', D: 100, T: [.15, .3, .4, .7] },
+ { c: 'soloman', s: 'skill1', m: 1, k: 'convoy', D: 520, T: [.4, .55, .7, .95] },
+ { c: 'soloman', s: 'skill2', m: 1, k: 'pull', D: 330, T: [.1, .25, .34, .6] },
+ { c: 'gufron', s: 'skill1', k: 'ants', D: 480, T: [.3, .6, 1.0, 1.4] },
+ { c: 'gufron', s: 'skill2', k: 'wave', D: 520, T: [.4, .5, .62, .8], base: 15 },
+ { c: 'gufron', s: 'skill1', m: 1, k: 'ants', D: 480, T: [.3, .6, 1.0, 1.4] },
+ { c: 'gufron', s: 'skill2', m: 1, k: 'bind', D: 480, T: [.4, .55, .7, .95] },
+];
+const OTHER = { megachan: 'browo', sonofsolo: 'browo', ethanolman: 'browo', anice: 'browo', soloman: 'browo', gufron: 'browo' };
+
+export default async ({ page, game, errors }) => {
+ fs.mkdirSync(OUT, { recursive: true });
+ const fails = [], log = [];
+ const check = (name, ok, info = '') => { if (!ok) fails.push(`${name} ${info}`); };
+
+ await game(() => {
+ // Pembantu di dalam halaman: menyiapkan satu jurus, melepasnya, lalu memajukan waktu sambil mencatat sampel.
+ window.__fxt = {
+ setup({ c, o, side, mode, D }) {
+ const g = window.__game; g.manual = true;
+ g.startMatch({ player: side === 'e' ? o : c, enemy: side === 'e' ? c : o, stage: 'bellora', level: 'easy', mode: 'training' });
+ g.setAI(false); if (side === 'e') g.setP2(true);
+ g.advance(1.5);
+ if (mode) { const k = side === 'e' ? '.' : 'p'; g.press(k); g.release(k); g.advance(7.6); }
+ g.hero.hp = 200; g.dummy.damage = 0; g.hero.invuln = 0; g.dummy.invuln = 0;
+ g.hero.cooldowns.skill1 = g.hero.cooldowns.skill2 = 0; g.dummy.cooldowns.skill1 = g.dummy.cooldowns.skill2 = 0;
+ if (side === 'e') { g.dummy.x = 930; g.hero.x = 930 - D; } else { g.hero.x = 350; g.dummy.x = 350 + D; }
+ g.advance(.2);
+ },
+ cast(side, skill) { const g = window.__game, k = side === 'e' ? (skill === 'skill1' ? 'm' : ',') : (skill === 'skill1' ? 'i' : 'o'); g.press(k); g.release(k); },
+ sample(side) {
+ const g = window.__game, t = side === 'e' ? g.hero : g.dummy, fx = g.fx.snapshot();
+ return { dmg: side === 'e' ? 200 - g.hero.hp : g.dummy.damage, x: t.x, y: t.y, st: side === 'e' ? g.hero.state : g.dummy.state, hurt: side === 'e' ? g.hero.hurtTime > 0 : g.dummy.state === 'hurt', fx };
+ },
+ };
+ });
+
+ for (const side of ['p', 'e']) for (const mv of MOVES) {
+ const name = `${side === 'p' ? 'pemain' : 'lawan'}:${mv.c}.${mv.m ? 'mode.' : ''}${mv.s}`;
+ if (process.env.FX_ONLY && !new RegExp(process.env.FX_ONLY).test(name)) continue;
+ await game(o => window.__fxt.setup(o), { c: mv.c, o: OTHER[mv.c], side, mode: !!mv.m, D: mv.D });
+ const x0 = await game(side => window.__fxt.sample(side).x, side);
+ await game(({ side, s }) => window.__fxt.cast(side, s), { side, s: mv.s });
+ const rows = []; let t = 0, shot = 0;
+ const end = Math.max(...mv.T) + (mv.k === 'dot' || mv.k === 'trail' ? 2.2 : mv.k === 'ants' ? 2.8 : .6);
+ while (t < end) {
+ await game(d => window.__game.advance(d), DT); t += DT;
+ const r = await game(side => window.__fxt.sample(side), side); r.t = +t.toFixed(3); rows.push(r);
+ if (shot < mv.T.length && t >= mv.T[shot] - 1e-6) { await page.screenshot({ path: path.join(OUT, `${side}_${mv.c}_${mv.m ? 'm_' : ''}${mv.s}_${shot}.png`), clip: { x: 0, y: 330, width: 1280, height: 290 } }); shot++; }
+ }
+ const dmgEnd = rows[rows.length - 1].dmg, firstHit = rows.find(r => r.dmg > 0)?.t ?? null;
+ const hitsBig = rows.reduce((n, r, i) => n + (i && r.dmg - rows[i - 1].dmg >= 5 ? 1 : 0), 0);
+ const moved = rows.some(r => r.fx.projectiles.some(p => p.age > .1)) || rows.some(r => r.fx.ghosts > 0 || r.fx.tele.hero >= 0 || r.fx.tele.dummy >= 0 || r.fx.effects.length);
+ log.push(`${name}: dmg ${dmgEnd} 1stHit ${firstHit} hits>=5 ${hitsBig}`);
+ check(name + ' efek tidak tampil/bergerak', moved);
+ check(name + ' tidak ada partikel berlebih', rows.every(r => r.fx.particles <= 470), String(Math.max(...rows.map(r => r.fx.particles))));
+
+ if (mv.k === 'wave' && !mv.m) {
+ // damage total tetap dalam +-10% dari angka sebelum efek dipoles (satu pukulan)
+ check(name + ' damage ' + dmgEnd, dmgEnd >= mv.base * .9 && dmgEnd <= mv.base * 1.1 + .01, `base ${mv.base}`);
+ const ids = new Set(rows.flatMap(r => r.fx.projectiles.map(p => p.fx)));
+ log.push(` gaya: ${[...ids].join(',')}`);
+ if (mv.lift) { const ground = rows[0].y; check(name + ' lawan terangkat', rows.some(r => r.y < ground - 20), 'y min ' + Math.min(...rows.map(r => r.y))); }
+ }
+ if (mv.k === 'area') {
+ const maxTele = Math.max(...rows.map(r => Math.max(r.fx.tele.hero, r.fx.tele.dummy)));
+ check(name + ' ada aba-aba cincin', maxTele > .9, String(maxTele));
+ const firstTele = rows.find(r => r.fx.tele.hero >= 0 || r.fx.tele.dummy >= 0)?.t;
+ check(name + ' aba-aba ~0,25 s sebelum kena', firstHit !== null && firstTele !== undefined && firstHit - firstTele > .18 && firstHit - firstTele < .32, `tele ${firstTele} hit ${firstHit}`);
+ check(name + ' kena di waktu semula (0,29-0,32 s)', firstHit !== null && firstHit >= .27 && firstHit <= .35, String(firstHit));
+ check(name + ' gelombang kejut + retakan', rows.some(r => r.fx.effects.includes('cf-shock')) && rows.some(r => r.fx.effects.includes('cf-crack')));
+ }
+ if (mv.k === 'dash') check(name + ' bayangan jejak', rows.some(r => r.fx.ghosts >= 3), String(Math.max(...rows.map(r => r.fx.ghosts))));
+ if (mv.k === 'rebound') { check(name + ' menembus lalu memantul (2 pukulan)', hitsBig === 2, String(hitsBig)); check(name + ' total ~35', dmgEnd >= 32 && dmgEnd <= 40, String(dmgEnd)); }
+ if (mv.k === 'trail') { check(name + ' genangan api', Math.max(...rows.map(r => r.fx.hazards)) >= 3, String(Math.max(...rows.map(r => r.fx.hazards)))); check(name + ' membakar berkala setelah kena', dmgEnd > 31, String(dmgEnd)); }
+ if (mv.k === 'split') { check(name + ' membelah jadi dua', rows.some(r => r.fx.projectiles.filter(p => p.fx === 'wave').length >= 2)); check(name + ' kedua belahan kena (total ~36)', dmgEnd >= 34 && dmgEnd <= 40, String(dmgEnd)); }
+ if (mv.k === 'dot') { check(name + ' korosi aktif', rows.some(r => r.fx.dots > 0)); const h = rows.findIndex(r => r.dmg > 0); check(name + ' damage berkala', h >= 0 && dmgEnd - rows[h + 20].dmg >= 4, `${rows[h + 20]?.dmg} -> ${dmgEnd}`); }
+ if (mv.k === 'bind') { const h = rows.findIndex(r => r.dmg > 0); const held = h >= 0 ? rows.slice(h, h + 33).filter(r => r.hurt).length * DT : 0; check(name + ' lawan terbelit >= 0,5 s', held >= .5, String(held)); }
+ if (mv.k === 'convoy') check(name + ' tiga mobil beruntun', hitsBig >= 3, String(hitsBig));
+ if (mv.k === 'aftershock') { check(name + ' ledakan kedua', hitsBig >= 2, String(hitsBig)); check(name + ' ledakan kedua tertunda', rows.some(r => r.fx.later > 0)); }
+ if (mv.k === 'pull') { const xs = rows.filter(r => r.t < .35).map(r => r.x); const pulled = Math.abs(xs[0] - xs[xs.length - 1]); check(name + ' lawan tersedot', pulled > 30, String(pulled)); }
+ if (mv.k === 'ants') {
+ check(name + ' semut mengitari dulu', rows.some(r => r.fx.projectiles.some(p => p.orbit)));
+ const n = Math.max(...rows.map(r => r.fx.projectiles.filter(p => p.fx === 'ant').length));
+ check(name + ' jumlah semut ' + n, n >= (mv.m ? 11 : 10), String(n));
+ check(name + ' total damage <= 45', dmgEnd <= 46, String(dmgEnd));
+ check(name + ' semut mengenai', dmgEnd >= 28, String(dmgEnd));
+ }
+ }
+
+ // Semut mengitari dulu lalu mengejar: tidak ada semut yang mengenai selama mengitari.
+ await game(o => window.__fxt.setup(o), { c: 'gufron', o: 'browo', side: 'p', mode: false, D: 150 });
+ await game(() => window.__fxt.cast('p', 'skill1'));
+ const early = await game(() => { const g = window.__game; g.advance(.5); return g.dummy.damage; });
+ check('semut tidak mengenai sebelum selesai mengitari (lawan di dekat)', early === 0, String(early));
+
+ // CPU memakai data jurus dan efek yang sama: 45 detik melawan pemain yang diam, lalu periksa gaya efek yang muncul.
+ const EXPECT = { megachan: ['rings', 'ghost'], sonofsolo: ['shout'], ethanolman: ['roll', 'cf-shock'], anice: ['bolt', 'cf-shock'], soloman: ['ghost', 'cf-shock'], gufron: ['ant', 'chain'] };
+ for (const [c, want] of Object.entries(EXPECT)) {
+ const seen = await game(({ c }) => {
+ const g = window.__game; g.manual = true; g.startMatch({ player: 'browo', enemy: c, stage: 'bellora', level: 'hard', mode: 'training' }); g.setAI(true);
+ const set = new Set();
+ for (let i = 0; i < 900; i++) { g.advance(.05); const f = g.fx.snapshot(); f.projectiles.forEach(p => set.add(p.fx)); f.effects.forEach(e => set.add(e)); if (f.ghosts) set.add('ghost'); if (g.hero.hp <= 0) g.hero.hp = 200; }
+ return [...set];
+ }, { c });
+ log.push(`CPU ${c}: ${seen.join(',')}`);
+ check(`CPU ${c} memakai efek jurus`, want.some(w => seen.includes(w)), `diharapkan salah satu dari ${want}, terlihat ${seen}`);
+ }
+
+ // Karakter lain (Bro Wo, Arco) tetap berfungsi: lepaskan semua jurus tanpa galat.
+ for (const c of ['browo']) await game(async c => { const g = window.__game; g.manual = true; g.startMatch({ player: c, enemy: 'megachan', stage: 'bellora', level: 'hard', mode: 'versus' }); g.setAI(true); for (let i = 0; i < 40; i++) { g.advance(.25); g.press('i'); g.release('i'); g.press('o'); g.release('o'); } }, c);
+
+ fs.writeFileSync(path.join(OUT, 'log.txt'), log.join('\n'));
+ if (fails.length) throw new Error('GAGAL (' + fails.length + '):\n' + fails.join('\n') + '\n\nLog:\n' + log.join('\n'));
+ return { ok: true, moves: MOVES.length * 2, shots: OUT, log };
+};