diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38ffb91..70cd7a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: with: python-version: "3.11" - name: Install dependencies - run: python -m pip install -r requirements.txt -r requirements-build.txt + run: python -m pip install -r requirements.txt - name: Build run: python -m PyInstaller --noconfirm --clean ComboTracker.spec - uses: actions/upload-artifact@v4 diff --git a/Game_Wuthering_Waves.py b/Game_Wuthering_Waves.py index 18f0a19..9c3c859 100644 --- a/Game_Wuthering_Waves.py +++ b/Game_Wuthering_Waves.py @@ -43,6 +43,10 @@ class WutheringWavesGame: # Global dash / RMB icon (shared across all teams) ww_dash_image: str = "" + # Your own notes per character (Characters page), keyed by the character id in + # static/data/ww_characters.json. They replace the shipped notes for that character. + ww_character_notes: dict[str, str] = field(default_factory=dict) + # Active character slot during an attempt ("1", "2", or "3"). ww_active_character: str | None = None @@ -205,6 +209,7 @@ def editor_payload(self, combo_name: str) -> dict[str, Any]: "ww_team_lmb_images": team_lmb_images, "ww_team_ability_images": team_ability_images, "ww_characters": ww_chars_list, + "ww_character_notes": dict(self.ww_character_notes), } # ------------------------- @@ -391,6 +396,21 @@ def delete_ww_character_cmd(engine, *, name: str) -> tuple[bool, str | None]: return True, None +def save_ww_character_note_cmd(engine, *, char_id: str, text: str) -> tuple[bool, str | None]: + cid = str(char_id or "").strip() + if not cid or len(cid) > 32: + return False, "Unknown character." + note = str(text or "").replace("\r\n", "\n").strip() + if len(note) > 20000: + return False, "That note is too long to save." + if engine.ww.ww_character_notes.get(cid) == note: + return True, None + engine.ww.ww_character_notes[cid] = note + engine.save_combos() + engine._send({"type": "ww_character_notes", "notes": dict(engine.ww.ww_character_notes)}) + return True, None + + def update_ww_dash_cmd(engine, *, dash_image: str) -> tuple[bool, str | None]: engine.ww.ww_dash_image = str(dash_image or "").strip() engine.save_combos() diff --git a/README.md b/README.md index b51eb64..4b19302 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,9 @@ Notes: ### Building the Windows release You can package ComboTracker so others can run it without installing Python. -1. Install build tooling (once): +1. Install the requirements, which include PyInstaller (once): ```bash - python -m pip install -r requirements-build.txt + python -m pip install -r requirements.txt ``` 2. From the project root, build: ```bash @@ -127,9 +127,10 @@ A built-in, NohBoard-style key display for streams and recordings: WASD, Shift, The **Characters** page (in the sidebar, or `http://localhost:8737/characters.html`) shows what each Wuthering Waves character's buttons do. It covers inputs only: no damage numbers. - **Moves**: the Basic Attack chain (A1, A2, ...), what holding LMB does, which hit LMB lands on after the Intro, Skill, Liberation, a Heavy, a Dodge Counter or a Mid-air Attack, and each move with its key. The timeline's move names use the same data. +- **Timings**: each ability's frame data from [WuwaLAB](https://wuwalab.com), laid out like WuwaLAB's Abilities tab: hits, total frames, the earliest cancel frame, how long until you can swap, time stop, motion stop, cooldown and the frame each hit lands on. Switch between frames and seconds (60 fps), and filter by name. - **Team rotations**: the community's team setups for that character with author and video, from [AntoCrasher's rotation compilation](https://docs.google.com/spreadsheets/d/1mdl9J08N-0_j-U2zNP5OTHGBKprwmJEmz_Iy4IOJfPk/edit). **Combo** opens the move-by-move rotation transcript, converts it to tracker inputs (swaps use the team's slot order; no timings), and **Save as combo** adds it to your combos with the video attached. -The move data ships with the app in `static/data/ww_characters.json`. It was drafted from the [encore.moe](https://encore.moe) API and checked by hand for the characters marked "Checked by hand"; edit that file to fix or add details. **Download raw data** saves encore.moe's unedited files to `data/encore_raw/` without changing anything shown; `python tools/ww_build_moves.py` then drafts any new characters from them (hand-checked entries are left alone). Rotations are downloaded when you open them and cached in `ww_library_cache/`. +The move data ships with the app in `static/data/ww_characters.json`. It was drafted from the [encore.moe](https://encore.moe) API and checked by hand for the characters marked "Checked by hand"; edit that file to fix or add details. **Download raw data** saves encore.moe's unedited files to `data/encore_raw/` without changing anything shown; `python tools/ww_build_moves.py` then drafts any new characters from them (hand-checked entries are left alone). The timings ship in `static/data/ww_timings.json`; to update them, scrape WuwaLAB's ability pages into one JSON file and run `python tools/ww_import_timings.py that-file.json` (damage columns are dropped). Rotations are downloaded when you open them and cached in `ww_library_cache/`. --- diff --git a/_combo_commands.py b/_combo_commands.py index 98e47e6..e65807f 100644 --- a/_combo_commands.py +++ b/_combo_commands.py @@ -1,243 +1,243 @@ -""" -UI command handlers: apply/save/delete combo, new combo, clear history. -Internal module: call only from ComboTrackerEngine while holding the engine lock. -""" - -from __future__ import annotations - -from typing import Any - -import combo_engine_ui as ui -import format_utils -import stats_recording -from parser import split_inputs -from persistence import fresh_combo_stats - - -def per_combo_maps(engine) -> list[dict[str, Any]]: - """Every dict keyed by combo name. Rename and delete go through this list so none is missed.""" - return [ - engine.combo_stats, - engine.combo_expected_ms, - engine.combo_user_difficulty, - engine.combo_step_display_mode, - engine.combo_key_images, - engine.combo_demo_video, - engine.ww.combo_ww_team, - ] - - -def apply_enders_from_text(engine, raw: str) -> tuple[bool, str | None]: - raw = (raw or "").strip() - if not raw: - engine.combo_enders = {} - setattr(engine, "combo_enders_soft", set()) - engine.save_combos() - return True, None - - parsed: dict[str, int] = {} - soft_keys: set[str] = set() - for token in split_inputs(raw): - t = token.strip() - if not t: - continue - if ":" in t: - k, v = t.split(":", 1) - raw_key = k.strip().lower() - # ~key:2s = soft ender (does not drop combo when pressed during hold) - if raw_key.startswith("~"): - raw_key = raw_key[1:].strip().lower() - if raw_key: - soft_keys.add(raw_key) - key = raw_key - val = (v or "").strip().lower() - if not key: - continue - # Require explicit "s" suffix for seconds: key:2s, key:0.2s - if val.endswith("s"): - val = val[:-1].strip() - else: - return False, f"Ender cooldown must be in seconds with 's' suffix, e.g. {key}:2s or {key}:0.2s" - try: - sec = float(val) - except ValueError: - return False, f"Invalid timing for '{key}'. Use seconds with 's', e.g. {key}:2s" - parsed[key] = max(0, int(sec * 1000)) - else: - key = t.strip().lower() - if key.startswith("~"): - key = key[1:].strip().lower() - if key: - soft_keys.add(key) - if key: - parsed[key] = 0 - - engine.combo_enders = parsed - engine.combo_enders_soft = soft_keys - engine.save_combos() - return True, None - - -def save_or_update_combo( - engine, - *, - name: str, - inputs: str, - enders: str | None, - expected_time: str | None = None, - user_difficulty: str | None = None, - step_display_mode: str | None = None, - key_images: Any | None = None, - demo_video: str | None = None, - ww_team_id: str | None = None, - as_new: bool = False, -) -> tuple[bool, str | None]: - name = (name or "").strip() - keys_str = (inputs or "").strip() - if not name or not keys_str: - return False, "Please fill in Name and Inputs." - - # Enders are global settings; a caller that doesn't send them (the Characters page) leaves them alone. - if enders is not None: - ok, err = apply_enders_from_text(engine, enders) - if not ok: - return False, err - - expected_ms = None - expected_raw = (expected_time or "").strip() - if expected_raw: - expected_ms = format_utils.parse_expected_time_ms(expected_raw) - if expected_ms is None: - return False, "Invalid Expected time. Examples: 1.05s or 1050ms" - - user_diff_val = None - ud_raw = (user_difficulty or "").strip() - if ud_raw: - try: - user_diff_val = float(ud_raw) - except Exception: - return False, "Invalid Your difficulty. Use a number from 0 to 10." - if not (0.0 <= user_diff_val <= 10.0): - return False, "Invalid Your difficulty. Use a number from 0 to 10." - - input_list = [k.strip().lower() for k in split_inputs(keys_str) if k.strip()] - if not input_list: - return False, "Please provide at least one input." - - # as_new: save a separate combo instead of editing (and renaming) the active one. - old_name = None if as_new else (engine.active_combo_name if engine.active_combo_name in engine.combos else None) - if name != old_name and name in engine.combos: - return False, f"A combo named '{name}' already exists. Pick another name." - if old_name and name != old_name: - # Rename: carry everything stored under the old name (stats included) to the new one. - del engine.combos[old_name] - engine.combos[name] = input_list - for mapping in per_combo_maps(engine): - if old_name in mapping: - mapping[name] = mapping.pop(old_name) - else: - # Same name or new combo: if steps changed, clear all history for this combo - old_list = engine.combos.get(name) - if old_list is not None and old_list != input_list: - engine.combo_stats[name] = fresh_combo_stats() - engine.combos[name] = input_list - - if expected_ms is not None: - engine.combo_expected_ms[name] = int(expected_ms) - else: - engine.combo_expected_ms.pop(name, None) - - if user_diff_val is not None: - engine.combo_user_difficulty[name] = float(user_diff_val) - else: - engine.combo_user_difficulty.pop(name, None) - - mode_raw = (step_display_mode or "").strip().lower() - if mode_raw in ("icons", "images"): - engine.combo_step_display_mode[name] = mode_raw - else: - engine.combo_step_display_mode.pop(name, None) - - cleaned_imgs: dict[str, str] = {} - if isinstance(key_images, dict): - for k, v in key_images.items(): - key = str(k).strip().lower() - url = str(v).strip() - if not key or not url: - continue - cleaned_imgs[key] = url - if cleaned_imgs: - engine.combo_key_images[name] = cleaned_imgs - else: - if isinstance(key_images, dict): - engine.combo_key_images.pop(name, None) - - demo_url = (demo_video or "").strip() - if demo_url: - engine.combo_demo_video[name] = demo_url - else: - engine.combo_demo_video.pop(name, None) - - engine.ww.apply_combo_team_assignment(name, ww_team_id=ww_team_id) - - stats_recording.ensure_combo_stats(engine, name) - engine.set_active_combo(name, emit=False) - engine.save_combos() - - engine._send({"type": "init", **engine.init_payload()}) - return True, None - - -def delete_combo(engine, name: str) -> tuple[bool, str | None]: - name = (name or "").strip() - if not name or name not in engine.combos: - return False, "Select a combo to delete." - - del engine.combos[name] - for mapping in per_combo_maps(engine): - mapping.pop(name, None) - - if engine.active_combo_name == name: - engine.active_combo_name = None - engine.active_combo_tokens = [] - engine.runtime_steps = [] - engine.reset_tracking() - - engine.save_combos() - engine._send({"type": "init", **engine.init_payload()}) - return True, None - - -def new_combo(engine) -> None: - engine.active_combo_name = None - engine.active_combo_tokens = [] - engine.runtime_steps = [] - engine.reset_tracking() - engine._send({"type": "init", **engine.init_payload()}) - - -def clear_history_and_stats(engine) -> None: - engine.reset_tracking() - if engine.active_combo_name: - engine.combo_stats[engine.active_combo_name] = fresh_combo_stats() - engine.save_combos() - engine._send({"type": "clear_results"}) - engine._send({"type": "stat_update", "stats": ui.stats_text(engine)}) - engine._send({"type": "fail_update", "fail_by_step": ui.failures_by_step(engine)}) - engine._send({"type": "timeline_update", "steps": engine.timeline_steps()}) - st = engine.get_status() - engine._send({"type": "status", "text": st.text, "color": st.color}) - - -def clear_all_history_and_stats(engine) -> None: - """Wipe history and stats for every combo.""" - engine.reset_tracking() - for name in list(engine.combos.keys()): - engine.combo_stats[name] = fresh_combo_stats() - engine.save_combos() - engine._send({"type": "clear_results"}) - engine._send({"type": "stat_update", "stats": ui.stats_text(engine)}) - engine._send({"type": "fail_update", "fail_by_step": ui.failures_by_step(engine)}) - engine._send({"type": "timeline_update", "steps": engine.timeline_steps()}) - st = engine.get_status() - engine._send({"type": "status", "text": st.text, "color": st.color}) +""" +UI command handlers: apply/save/delete combo, new combo, clear history. +Internal module: call only from ComboTrackerEngine while holding the engine lock. +""" + +from __future__ import annotations + +from typing import Any + +import combo_engine_ui as ui +import format_utils +import stats_recording +from parser import lower_outside_quotes, split_inputs +from persistence import fresh_combo_stats + + +def per_combo_maps(engine) -> list[dict[str, Any]]: + """Every dict keyed by combo name. Rename and delete go through this list so none is missed.""" + return [ + engine.combo_stats, + engine.combo_expected_ms, + engine.combo_user_difficulty, + engine.combo_step_display_mode, + engine.combo_key_images, + engine.combo_demo_video, + engine.ww.combo_ww_team, + ] + + +def apply_enders_from_text(engine, raw: str) -> tuple[bool, str | None]: + raw = (raw or "").strip() + if not raw: + engine.combo_enders = {} + setattr(engine, "combo_enders_soft", set()) + engine.save_combos() + return True, None + + parsed: dict[str, int] = {} + soft_keys: set[str] = set() + for token in split_inputs(raw): + t = token.strip() + if not t: + continue + if ":" in t: + k, v = t.split(":", 1) + raw_key = k.strip().lower() + # ~key:2s = soft ender (does not drop combo when pressed during hold) + if raw_key.startswith("~"): + raw_key = raw_key[1:].strip().lower() + if raw_key: + soft_keys.add(raw_key) + key = raw_key + val = (v or "").strip().lower() + if not key: + continue + # Require explicit "s" suffix for seconds: key:2s, key:0.2s + if val.endswith("s"): + val = val[:-1].strip() + else: + return False, f"Ender cooldown must be in seconds with 's' suffix, e.g. {key}:2s or {key}:0.2s" + try: + sec = float(val) + except ValueError: + return False, f"Invalid timing for '{key}'. Use seconds with 's', e.g. {key}:2s" + parsed[key] = max(0, int(sec * 1000)) + else: + key = t.strip().lower() + if key.startswith("~"): + key = key[1:].strip().lower() + if key: + soft_keys.add(key) + if key: + parsed[key] = 0 + + engine.combo_enders = parsed + engine.combo_enders_soft = soft_keys + engine.save_combos() + return True, None + + +def save_or_update_combo( + engine, + *, + name: str, + inputs: str, + enders: str | None, + expected_time: str | None = None, + user_difficulty: str | None = None, + step_display_mode: str | None = None, + key_images: Any | None = None, + demo_video: str | None = None, + ww_team_id: str | None = None, + as_new: bool = False, +) -> tuple[bool, str | None]: + name = (name or "").strip() + keys_str = (inputs or "").strip() + if not name or not keys_str: + return False, "Please fill in Name and Inputs." + + # Enders are global settings; a caller that doesn't send them (the Characters page) leaves them alone. + if enders is not None: + ok, err = apply_enders_from_text(engine, enders) + if not ok: + return False, err + + expected_ms = None + expected_raw = (expected_time or "").strip() + if expected_raw: + expected_ms = format_utils.parse_expected_time_ms(expected_raw) + if expected_ms is None: + return False, "Invalid Expected time. Examples: 1.05s or 1050ms" + + user_diff_val = None + ud_raw = (user_difficulty or "").strip() + if ud_raw: + try: + user_diff_val = float(ud_raw) + except Exception: + return False, "Invalid Your difficulty. Use a number from 0 to 10." + if not (0.0 <= user_diff_val <= 10.0): + return False, "Invalid Your difficulty. Use a number from 0 to 10." + + input_list = [lower_outside_quotes(k.strip()) for k in split_inputs(keys_str) if k.strip()] + if not input_list: + return False, "Please provide at least one input." + + # as_new: save a separate combo instead of editing (and renaming) the active one. + old_name = None if as_new else (engine.active_combo_name if engine.active_combo_name in engine.combos else None) + if name != old_name and name in engine.combos: + return False, f"A combo named '{name}' already exists. Pick another name." + if old_name and name != old_name: + # Rename: carry everything stored under the old name (stats included) to the new one. + del engine.combos[old_name] + engine.combos[name] = input_list + for mapping in per_combo_maps(engine): + if old_name in mapping: + mapping[name] = mapping.pop(old_name) + else: + # Same name or new combo: if steps changed, clear all history for this combo + old_list = engine.combos.get(name) + if old_list is not None and old_list != input_list: + engine.combo_stats[name] = fresh_combo_stats() + engine.combos[name] = input_list + + if expected_ms is not None: + engine.combo_expected_ms[name] = int(expected_ms) + else: + engine.combo_expected_ms.pop(name, None) + + if user_diff_val is not None: + engine.combo_user_difficulty[name] = float(user_diff_val) + else: + engine.combo_user_difficulty.pop(name, None) + + mode_raw = (step_display_mode or "").strip().lower() + if mode_raw in ("icons", "images"): + engine.combo_step_display_mode[name] = mode_raw + else: + engine.combo_step_display_mode.pop(name, None) + + cleaned_imgs: dict[str, str] = {} + if isinstance(key_images, dict): + for k, v in key_images.items(): + key = str(k).strip().lower() + url = str(v).strip() + if not key or not url: + continue + cleaned_imgs[key] = url + if cleaned_imgs: + engine.combo_key_images[name] = cleaned_imgs + else: + if isinstance(key_images, dict): + engine.combo_key_images.pop(name, None) + + demo_url = (demo_video or "").strip() + if demo_url: + engine.combo_demo_video[name] = demo_url + else: + engine.combo_demo_video.pop(name, None) + + engine.ww.apply_combo_team_assignment(name, ww_team_id=ww_team_id) + + stats_recording.ensure_combo_stats(engine, name) + engine.set_active_combo(name, emit=False) + engine.save_combos() + + engine._send({"type": "init", **engine.init_payload()}) + return True, None + + +def delete_combo(engine, name: str) -> tuple[bool, str | None]: + name = (name or "").strip() + if not name or name not in engine.combos: + return False, "Select a combo to delete." + + del engine.combos[name] + for mapping in per_combo_maps(engine): + mapping.pop(name, None) + + if engine.active_combo_name == name: + engine.active_combo_name = None + engine.active_combo_tokens = [] + engine.runtime_steps = [] + engine.reset_tracking() + + engine.save_combos() + engine._send({"type": "init", **engine.init_payload()}) + return True, None + + +def new_combo(engine) -> None: + engine.active_combo_name = None + engine.active_combo_tokens = [] + engine.runtime_steps = [] + engine.reset_tracking() + engine._send({"type": "init", **engine.init_payload()}) + + +def clear_history_and_stats(engine) -> None: + engine.reset_tracking() + if engine.active_combo_name: + engine.combo_stats[engine.active_combo_name] = fresh_combo_stats() + engine.save_combos() + engine._send({"type": "clear_results"}) + engine._send({"type": "stat_update", "stats": ui.stats_text(engine)}) + engine._send({"type": "fail_update", "fail_by_step": ui.failures_by_step(engine)}) + engine._send({"type": "timeline_update", "steps": engine.timeline_steps()}) + st = engine.get_status() + engine._send({"type": "status", "text": st.text, "color": st.color}) + + +def clear_all_history_and_stats(engine) -> None: + """Wipe history and stats for every combo.""" + engine.reset_tracking() + for name in list(engine.combos.keys()): + engine.combo_stats[name] = fresh_combo_stats() + engine.save_combos() + engine._send({"type": "clear_results"}) + engine._send({"type": "stat_update", "stats": ui.stats_text(engine)}) + engine._send({"type": "fail_update", "fail_by_step": ui.failures_by_step(engine)}) + engine._send({"type": "timeline_update", "steps": engine.timeline_steps()}) + st = engine.get_status() + engine._send({"type": "status", "text": st.text, "color": st.color}) diff --git a/combo_engine.py b/combo_engine.py index 4c1af67..7eef71c 100644 --- a/combo_engine.py +++ b/combo_engine.py @@ -17,6 +17,7 @@ save_ww_character_cmd, delete_ww_character_cmd, update_ww_dash_cmd, + save_ww_character_note_cmd, ) import combo_analytics import combo_engine_ui as ui @@ -437,6 +438,10 @@ def delete_ww_character(self, name: str) -> tuple[bool, str | None]: with self._lock: return delete_ww_character_cmd(self, name=name) + def save_ww_character_note(self, char_id: str, text: str) -> tuple[bool, str | None]: + with self._lock: + return save_ww_character_note_cmd(self, char_id=char_id, text=text) + def update_ww_dash(self, dash_image: str) -> tuple[bool, str | None]: with self._lock: return update_ww_dash_cmd(self, dash_image=dash_image) diff --git a/combo_engine_ui.py b/combo_engine_ui.py index 9487195..bcb9d6f 100644 --- a/combo_engine_ui.py +++ b/combo_engine_ui.py @@ -322,6 +322,8 @@ def _render_sequence_items( prog = _wait_progress(nxt, now) if prog is not None: pw["progress"] = prog + if nxt.optional: + pw["wait_optional"] = True seq_items.append(pw) j += 2 continue @@ -341,6 +343,8 @@ def _render_sequence_items( prog = _wait_progress(sub, now) if prog is not None: wait_dict["progress"] = prog + if sub.optional: + wait_dict["optional"] = True seq_items.append(wait_dict) elif isinstance(sub, HoldState): seq_items.append( @@ -482,13 +486,16 @@ def _timeline_steps_from_runtime(engine, now: float | None = None) -> TimelineSt if len(seq.steps) >= 2 and isinstance(seq.steps[1], WaitState): dur = seq.steps[1].required_ms pw_active = step.active_item is item - items_payload.append({ + pw_item = { "type": "press_wait", "input": inp, "duration": dur, "active": (idx == cur) and pw_active, "completed": comp, - }) + } + if len(seq.steps) >= 2 and isinstance(seq.steps[1], WaitState) and seq.steps[1].optional: + pw_item["wait_optional"] = True + items_payload.append(pw_item) elif item.kind == "sequence" and isinstance(item.state, SequenceState): seq = item.state parent_is_active = bool((idx == cur) and (step.active_item is item)) @@ -545,6 +552,8 @@ def _timeline_steps_from_runtime(engine, now: float | None = None) -> TimelineSt if getattr(step, "optional", False): pw["optional"] = True pw["was_skipped"] = getattr(step, "was_skipped", False) + if nxt.optional: + pw["wait_optional"] = True steps.append(pw) i += 1 continue @@ -635,6 +644,8 @@ def _timeline_steps_from_runtime(engine, now: float | None = None) -> TimelineSt prog = _wait_progress(step, now) if prog is not None: w["progress"] = prog + if step.optional: + w["optional"] = True steps.append(w) i += 1 continue diff --git a/documentation.md b/documentation.md index f992e1a..7cc4199 100644 --- a/documentation.md +++ b/documentation.md @@ -41,6 +41,14 @@ Prefix a key with `-` to make it optional: `-e` means you may skip that key. If - Example: `f, wait:0.3s, -e, wait:0.80s, r, wait:1.8s` — you can do `f`, wait, then `r` (skipping `e`) or `f`, wait, `e`, wait, `r`. In the timeline, optional steps use a dashed border; skipped ones show a subtle “skipped” style, pressed ones show “was-pressed”. +### Picking which move a step is + +Some keys can mean more than one move, and the keys alone can't tell which: LMB after a dodge is either a Dodge Counter or Basic 1, a held LMB can be Heavy 1 or Heavy 2, and so on. Right-click the step on the timeline and pick the move from the list (the moves that key can cast for that character, from WuwaLAB, best guess first). The list leaves out moves the step before rules out (after a dodge, LMB is the Dodge Counter or Basic 1; after a jump, only Mid-Air moves). The pick is saved in the inputs as a quoted name after the key, e.g. `rmb, lmb "Basic: Origin Calculus 1", wait:0.15s`, and you can type it by hand too. It only changes the move name on the timeline, the Basic chain count after it and the Concerto estimate; the tracker ignores it when checking your presses. For characters WuwaLAB covers, every step is named with WuwaLAB's name for the move: the best guess until you pick another. The move in use is always checked; picking the best guess again removes the saved name. Names can't contain double quotes. + +### Optional waits + +Prefix a wait with `-` to make it optional: `-wait:0.15s`. Pressing any key during that wait ends it early and the press counts for the next step, so you can either wait it out or go straight on. Like `wait:`, right after a key it joins that key's tile (`e, -wait:0.15s, r`: press `e`, then `r` whenever you like), and elsewhere it is its own tile (`hold(lmb, 0.5s), -wait:0.2s, e`). Plain `wait:` is unchanged. In the timeline, an optional wait has a dashed right edge. + ### Any-order groups (order interchangeable) Use brackets to indicate that multiple inputs can be pressed in **any order**, but all must be completed before the combo continues: diff --git a/parser.py b/parser.py index 75d4e0e..adb71ab 100644 --- a/parser.py +++ b/parser.py @@ -5,6 +5,7 @@ AST to dict format for the existing engine (compatibility layer). """ +import re from dataclasses import dataclass from typing import Any, Literal from uuid import uuid4 @@ -50,6 +51,7 @@ class WaitNode: duration_ms: int mode: Literal["soft", "hard", "mandatory"] wait_for: str | None = None # for mandatory waits + optional: bool = False # -wait:Xs: any key press during the wait ends it early (no fail) @dataclass(frozen=True) @@ -80,14 +82,22 @@ def split_inputs(keys_str: str) -> list[str]: """ Split a user-entered Inputs string into top-level comma-separated tokens. - Shallow parser: avoids splitting commas inside (), {}, []. + Shallow parser: avoids splitting commas inside (), {}, [] and "move names". """ s = keys_str or "" out: list[str] = [] buf: list[str] = [] paren = brace = bracket = 0 + quoted = False for ch in s: + if ch == '"': + quoted = not quoted + buf.append(ch) + continue + if quoted: + buf.append(ch) + continue if ch == "(": paren += 1 elif ch == ")": @@ -115,6 +125,26 @@ def split_inputs(keys_str: str) -> list[str]: return out +# A step can name the move it casts: lmb "Basic: Origin Calculus 2 (Dodge Counter)". The name is +# for the timeline label only (picked from the step's right-click menu); the engine ignores it. +_MOVE_NAME_RE = re.compile(r'\s*"([^"]*)"\s*$') + + +def split_move_name(token: str) -> tuple[str, str | None]: + """'lmb "Basic: X 1"' -> ('lmb', 'Basic: X 1'); a token without a name -> (token, None).""" + t = token or "" + m = _MOVE_NAME_RE.search(t) + if not m: + return t, None + return t[: m.start()], m.group(1) + + +def lower_outside_quotes(token: str) -> str: + """Lowercase the keys of a token but keep a "Move Name" as written.""" + parts = (token or "").split('"') + return '"'.join(p.lower() if i % 2 == 0 else p for i, p in enumerate(parts)) + + def _parse_duration(raw: str) -> int | None: token = (raw or "").lower().strip() if not token: @@ -150,7 +180,7 @@ def parse_step(token: str) -> StepNode | None: Returns None for empty/invalid tokens. """ - t = (token or "").strip() + t = split_move_name((token or "").strip())[0].strip() if not t: return None @@ -273,11 +303,13 @@ def parse_step(token: str) -> StepNode | None: return GroupNode(tuple(items)) # Wait gate: wait:0.1 (only form; use after a key e.g. f, wait:0.23s) - if tl.startswith("wait:"): - dur = tl[len("wait:"):].strip() + # Optional wait: -wait:0.15s, which the next press may cut short. + optional_wait = tl.startswith("-wait:") + if tl.startswith("wait:") or optional_wait: + dur = tl[tl.index(":") + 1:].strip() wait_ms = _parse_duration(dur) if wait_ms is not None: - return WaitNode(wait_ms, "soft", None) + return WaitNode(wait_ms, "soft", None, optional=optional_wait) # Repeated taps over a duration: spam(lmb, 4.4s) if tl.startswith("spam(") and tl.endswith(")"): diff --git a/persistence.py b/persistence.py index 7fb33da..2580a5d 100644 --- a/persistence.py +++ b/persistence.py @@ -465,6 +465,10 @@ def load_engine_state(engine) -> None: _load_combo_options(engine, data) _load_ww_characters(engine, data) _load_ww_teams(engine, data) + notes = data.get("ww_character_notes") + engine.ww.ww_character_notes = { + str(k): str(v) for k, v in notes.items() if isinstance(v, str) + } if isinstance(notes, dict) else {} _migrate_legacy_combo_ability_images(engine, data) _load_macro_settings(engine, data) @@ -485,6 +489,7 @@ def load_engine_state(engine) -> None: engine.combo_demo_video = {} engine.ww.ww_characters = {} engine.ww.ww_dash_image = "" + engine.ww.ww_character_notes = {} engine.ww.ww_teams = {} engine.ww.ww_active_team_id = None engine.ww.combo_ww_team = {} @@ -525,6 +530,7 @@ def save_engine_state(engine) -> bool: "combo_demo_video": dict(getattr(engine, "combo_demo_video", {})), "ww_characters": dict(engine.ww.ww_characters), "ww_dash_image": engine.ww.ww_dash_image, + "ww_character_notes": dict(getattr(engine.ww, "ww_character_notes", {})), "ww_teams": { tid: { "name": tv.get("name", "Team"), diff --git a/states.py b/states.py index de59a9c..8259943 100644 --- a/states.py +++ b/states.py @@ -162,6 +162,7 @@ def to_dict(self) -> dict: "type": "spam", "input": self.expected, "duration": self.required_ms, + "optional": self.optional, "in_progress": self.in_progress, "completed": self.completed, } @@ -373,6 +374,7 @@ class WaitState: required_ms: int mode: Literal["soft", "hard", "mandatory"] wait_for: str | None = None # for mandatory waits + optional: bool = False # -wait:Xs: a press during the wait ends it and counts for the next step # Runtime mutable state in_progress: bool = False @@ -400,6 +402,12 @@ def process_press(self, key: str, now: float) -> ProcessResult: if self.mode == "mandatory": return IgnoreResult() + # Optional wait: the press ends it early and is re-processed against the next step. + if self.optional: + self.completed = True + self.in_progress = False + return CompleteResult() + if self.mode == "hard": return FailResult("early press during hard wait") @@ -788,7 +796,7 @@ def build_runtime_state(node: StepNode) -> StepState: return SpamState(expected=key, required_ms=ms) case WaitNode(duration_ms=ms, mode=mode, wait_for=wf): - return WaitState(required_ms=ms, mode=mode, wait_for=wf) + return WaitState(required_ms=ms, mode=mode, wait_for=wf, optional=getattr(node, "optional", False)) case SequenceNode(steps=steps): is_mandatory = _is_composite_mandatory(node) diff --git a/static/app/combos.js b/static/app/combos.js index 18cccf7..db1e1b9 100644 --- a/static/app/combos.js +++ b/static/app/combos.js @@ -15,7 +15,6 @@ function initializeUI(data) { wwTeamId: appState.wwTeamId, stepDisplayMode: appState.stepDisplayMode, noFailMode: !!getEl('noFailMode')?.checked, - stepEditMode: !!getEl('stepEditToggle')?.checked, collapseChainedPresses: !!getEl('collapseChainsToggle')?.checked, keyImages: { ...appState.keyImages }, } : null; @@ -31,9 +30,6 @@ function initializeUI(data) { if (stepToggle) stepToggle.checked = (appState.stepDisplayMode === 'images'); const noFailEl = getEl('noFailMode'); if (noFailEl) noFailEl.checked = preserved.noFailMode; - appState.stepEditMode = preserved.stepEditMode; - const stepEditToggle = getEl('stepEditToggle'); - if (stepEditToggle) stepEditToggle.checked = preserved.stepEditMode; appState.collapseChainedPresses = preserved.collapseChainedPresses; const collapseChainsToggle = getEl('collapseChainsToggle'); if (collapseChainsToggle) collapseChainsToggle.checked = preserved.collapseChainedPresses; @@ -222,10 +218,10 @@ document.addEventListener('keydown', (e) => { } }); -// Ctrl/Cmd+Z: undo the last Edit Steps mutation (delete, reorder, or inline field edit) +// Ctrl/Cmd+Z: undo the last step edit (delete, reorder, or inline field edit) document.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey && e.key.toLowerCase() === 'z') { - if (!appState.stepEditMode || appState.editStepsUndoStack.length === 0) return; + if (appState.editStepsUndoStack.length === 0) return; e.preventDefault(); undoLastEditStep(); } @@ -407,10 +403,10 @@ function tokenizeComboInput(text) { tokens.push({ type: 'hold', text: text.slice(start, i) }); continue; } - // wait:duration (soft wait) - if (text.slice(i).match(/^wait\s*:/)) { + // wait:duration (soft wait), -wait:duration (optional wait) + if (text.slice(i).match(/^-?wait\s*:/)) { const start = i; - i += text.slice(i).match(/^wait\s*:/)[0].length; + i += text.slice(i).match(/^-?wait\s*:/)[0].length; while (i < len && /[^\s,\[\]\{\}]/.test(text[i])) i++; tokens.push({ type: 'wait', text: text.slice(start, i) }); continue; @@ -423,6 +419,14 @@ function tokenizeComboInput(text) { tokens.push({ type: 'optional', text: text.slice(start, i) }); continue; } + // "Move Name" picked from a step's right-click menu + if (text[i] === '"') { + const close = text.indexOf('"', i + 1); + const end = close === -1 ? len : close + 1; + tokens.push({ type: 'name', text: text.slice(i, end) }); + i = end; + continue; + } // brackets and braces if (text[i] === '[' || text[i] === ']') { tokens.push({ type: 'bracket', text: text[i] }); @@ -451,12 +455,24 @@ function tokenizeComboInput(text) { }).join(''); } +// [start, end) of the Inputs text to mark, for the timeline tile being dragged or selected +// (see "Tile focus" below), or null. +let comboInputMark = null; + function updateComboInputHighlight() { const ta = getEl('comboInputs'); const mirror = getEl('comboInputHighlight'); if (!ta || !mirror) return; const raw = (ta.value || ''); - mirror.innerHTML = raw ? tokenizeComboInput(raw) : ''; + const m = comboInputMark; + if (raw && m && m.text === raw && m.start < m.end) { + // Marks fall on top-level token boundaries, so each slice tokenizes the same as the whole. + mirror.innerHTML = tokenizeComboInput(raw.slice(0, m.start)) + + `${tokenizeComboInput(raw.slice(m.start, m.end))}` + + tokenizeComboInput(raw.slice(m.end)); + } else { + mirror.innerHTML = raw ? tokenizeComboInput(raw) : ''; + } mirror.scrollTop = ta.scrollTop; mirror.scrollLeft = ta.scrollLeft; } @@ -486,6 +502,7 @@ function splitInputsTokenSpans(str) { const spans = []; let depth = 0; let start = 0; + let quoted = false; // inside a "Move Name" const push = (end) => { const raw = str.slice(start, end); const lead = raw.length - raw.trimStart().length; @@ -494,6 +511,8 @@ function splitInputsTokenSpans(str) { }; for (let i = 0; i < str.length; i++) { const ch = str[i]; + if (ch === '"') { quoted = !quoted; continue; } + if (quoted) continue; if (ch === '(' || ch === '{' || ch === '[') depth++; else if (ch === ')' || ch === '}' || ch === ']') depth = Math.max(0, depth - 1); else if (ch === ',' && depth === 0) { push(i); start = i + 1; } @@ -545,3 +564,141 @@ if (inputsEl) { const timeline = getEl('comboTimeline'); if (timeline) new MutationObserver(() => { if (document.activeElement === inputsEl) applyEditFocus(); }).observe(timeline, { childList: true }); } + +// --------------------------------------------------------------------------- +// Tile focus: the reverse of editing focus. Dragging a Combo Steps tile, or clicking one, marks +// its text in the Inputs box. Like editing focus it only follows tokens that still line up +// with the saved combo. +// --------------------------------------------------------------------------- + +/** [start, end) of the Inputs text behind these runtime steps, or null when it can't be matched. */ +function inputRangeForRuntime(runtimeIndices) { + const ta = getEl('comboInputs'); + if (!ta || !runtimeIndices.length) return null; + const saved = splitInputsTokens(appState.savedInputs || ''); + const srcMap = buildRuntimeToSourceMap(saved); + const tokIdx = new Set(); + runtimeIndices.forEach((r) => (srcMap[r] || []).forEach((t) => tokIdx.add(t))); + if (!tokIdx.size) return null; + const spans = splitInputsTokenSpans(ta.value || ''); + const last = Math.max(...tokIdx); + for (let i = 0; i <= last; i++) { + if (!spans[i] || (saved[i] || '').toLowerCase() !== spans[i].text.toLowerCase()) return null; + } + const first = Math.min(...tokIdx); + return { start: spans[first].start, end: spans[last].end, text: ta.value || '' }; +} + +function setInputMarkForTile(tile) { + const raw = tile ? (tile.dataset.stepIndices || '').trim() : ''; + const indices = raw ? raw.split(',').map((v) => Number.parseInt(v, 10)).filter(Number.isFinite) : []; + comboInputMark = inputRangeForRuntime(indices); + updateComboInputHighlight(); + if (!comboInputMark) return; + // Bring the marked text into view inside the Inputs box. + const ta = getEl('comboInputs'); + const mark = getEl('comboInputHighlight')?.querySelector('.combo-hl-focus'); + if (!ta || !mark) return; + const top = mark.offsetTop; + if (top < ta.scrollTop || top + mark.offsetHeight > ta.scrollTop + ta.clientHeight) { + ta.scrollTop = Math.max(0, top - ta.clientHeight / 3); + updateComboInputHighlight(); + } +} + +{ + const timeline = getEl('comboTimeline'); + const topTile = (target) => (timeline && target ? [...timeline.children].find((c) => c.contains(target)) : null); + if (timeline) { + timeline.addEventListener('dragstart', (ev) => setInputMarkForTile(topTile(ev.target))); + timeline.addEventListener('dragend', () => { comboInputMark = null; updateComboInputHighlight(); }); + timeline.addEventListener('click', (ev) => { + if (ev.target.closest('button, input, [contenteditable="true"]')) return; + const tile = topTile(ev.target); + timeline.querySelectorAll('.step-selected').forEach((el) => el.classList.remove('step-selected')); + if (tile && tile.dataset.stepIndices) tile.classList.add('step-selected'); + setInputMarkForTile(tile); + }); + } + // Clicking anywhere else clears the mark; typing in the Inputs box drops it (offsets move). + document.addEventListener('click', (ev) => { + if (!comboInputMark || (timeline && timeline.contains(ev.target))) return; + if (ev.target.closest && ev.target.closest('#comboInputs')) return; + comboInputMark = null; + timeline?.querySelectorAll('.step-selected').forEach((el) => el.classList.remove('step-selected')); + updateComboInputHighlight(); + }); + getEl('comboInputs')?.addEventListener('input', () => { comboInputMark = null; }); +} + +// --------------------------------------------------------------------------- +// Copy and paste tiles: Ctrl+C copies the selected tile's inputs, Ctrl+V pastes them next to +// the tile under (or nearest) the mouse: before it on its left half, after it on its right. +// Text boxes keep their own copy and paste. +// --------------------------------------------------------------------------- + +let tileClipboard = null; // input tokens of the copied tile +const mousePos = { x: -1, y: -1 }; +document.addEventListener('mousemove', (ev) => { mousePos.x = ev.clientX; mousePos.y = ev.clientY; }, { passive: true }); + +/** Source token indices (in the Inputs box) behind a timeline tile, sorted, or []. */ +function tokenIndicesForTile(tile, tokens) { + const raw = tile ? (tile.dataset.stepIndices || '').trim() : ''; + if (!raw) return []; + const srcMap = buildRuntimeToSourceMap(tokens); + const out = new Set(); + raw.split(',').forEach((v) => (srcMap[Number.parseInt(v, 10)] || []).forEach((t) => out.add(t))); + return [...out].sort((a, b) => a - b); +} + +function isTypingTarget(el) { + return !!(el && (el.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName))); +} + +document.addEventListener('keydown', (ev) => { + if (!(ev.ctrlKey || ev.metaKey) || ev.shiftKey || ev.altKey) return; + const k = ev.key.toLowerCase(); + if (k !== 'c' && k !== 'v') return; + if (isTypingTarget(document.activeElement)) return; + if (k === 'c' && String(window.getSelection?.() || '').trim()) return; // copying page text + const timeline = getEl('comboTimeline'); + const inputsEl = getEl('comboInputs'); + if (!timeline || !inputsEl) return; + const tokens = splitInputsTokens(inputsEl.value || ''); + + if (k === 'c') { + const tile = timeline.querySelector('.step-selected'); + const idx = tokenIndicesForTile(tile, tokens); + if (!idx.length) return; + ev.preventDefault(); + tileClipboard = idx.map((i) => tokens[i]); + navigator.clipboard?.writeText(tileClipboard.join(', ')).catch(() => {}); + if (typeof showToast === 'function') showToast('Step copied. Point at a spot and press Ctrl+V.'); + return; + } + + if (!tileClipboard || !tileClipboard.length) return; + // The tile under the mouse, else the nearest one; else the end of the combo. + const tiles = [...timeline.children].filter((c) => (c.dataset.stepIndices || '').trim()); + let target = null; + let best = Infinity; + tiles.forEach((t) => { + const r = t.getBoundingClientRect(); + const dx = Math.max(r.left - mousePos.x, 0, mousePos.x - r.right); + const dy = Math.max(r.top - mousePos.y, 0, mousePos.y - r.bottom); + const d = dx * dx + dy * dy; + if (d < best) { best = d; target = t; } + }); + let at = tokens.length; + if (target) { + const idx = tokenIndicesForTile(target, tokens); + if (idx.length) { + const r = target.getBoundingClientRect(); + at = mousePos.x < r.left + r.width / 2 ? idx[0] : idx[idx.length - 1] + 1; + } + } + ev.preventDefault(); + tokens.splice(at, 0, ...tileClipboard); + pushEditStepsUndoSnapshot(); + saveComboInputs(tokens.join(', ')); +}); diff --git a/static/app/core.js b/static/app/core.js index 7be6c78..820ea32 100644 --- a/static/app/core.js +++ b/static/app/core.js @@ -49,7 +49,6 @@ const appState = { collapseChainedPresses: true, showMoveNames: readStoredFlag('showMoveNames', true), autoScrollEnabled: false, - stepEditMode: true, editStepsUndoStack: [], targetGame: 'wuthering_waves', wwAbilityImages: { "1": {}, "2": {}, "3": {} }, diff --git a/static/app/practice.js b/static/app/practice.js index 593fde1..68982b7 100644 --- a/static/app/practice.js +++ b/static/app/practice.js @@ -373,15 +373,6 @@ if (moveNamesToggleEl) { }); } -const stepEditToggleEl = getEl('stepEditToggle'); -if (stepEditToggleEl) { - stepEditToggleEl.checked = !!appState.stepEditMode; - stepEditToggleEl.addEventListener('change', () => { - appState.stepEditMode = stepEditToggleEl.checked; - refreshTimelineIfLoaded(); - }); -} - const collapseChainsToggleEl = getEl('collapseChainsToggle'); if (collapseChainsToggleEl) { collapseChainsToggleEl.checked = !!appState.collapseChainedPresses; diff --git a/static/app/shell.js b/static/app/shell.js index 1a2bf29..e258f47 100644 --- a/static/app/shell.js +++ b/static/app/shell.js @@ -57,6 +57,39 @@ getEl('railToggle')?.addEventListener('click', () => { writeStoredFlag('ctRailCompact', compact); }); +// Drag the rail's right edge to resize it (remembered per browser); double-click resets. +const RAIL_WIDTH_KEY = 'ctRailWidth'; +const RAIL_MIN = 200, RAIL_MAX = 480, RAIL_DEFAULT = 256; +function setRailWidth(px) { + const w = Math.round(Math.min(RAIL_MAX, Math.max(RAIL_MIN, px))); + appEl.style.setProperty('--rail-w', `${w}px`); + return w; +} +try { + const saved = Number(localStorage.getItem(RAIL_WIDTH_KEY)); + if (saved) setRailWidth(saved); +} catch (_) { /* ignore */ } +const railResizer = getEl('railResizer'); +railResizer?.addEventListener('pointerdown', (e) => { + e.preventDefault(); + railResizer.setPointerCapture(e.pointerId); + document.body.classList.add('resizing'); + let width = Math.round(document.querySelector('.rail').getBoundingClientRect().width); + const move = (ev) => { width = setRailWidth(ev.clientX); }; + const up = () => { + railResizer.removeEventListener('pointermove', move); + document.body.classList.remove('resizing'); + try { localStorage.setItem(RAIL_WIDTH_KEY, String(width)); } catch (_) { /* ignore */ } + }; + railResizer.addEventListener('pointermove', move); + railResizer.addEventListener('pointerup', up, { once: true }); + railResizer.addEventListener('pointercancel', up, { once: true }); +}); +railResizer?.addEventListener('dblclick', () => { + setRailWidth(RAIL_DEFAULT); + try { localStorage.removeItem(RAIL_WIDTH_KEY); } catch (_) { /* ignore */ } +}); + // The combo list lives in the rail; without a rail (phones) it moves to the top of the Practice page. const comboListCard = getEl('comboListCard'); const railSpacer = document.querySelector('.rail-spacer'); @@ -426,6 +459,16 @@ fetch('data/ww_characters.json') }) .catch(() => { /* move names fall back to generic rules */ }); +// WuwaLAB's per-ability Concerto, so the timeline can tell when a character's bar is full. +fetch('data/ww_timings.json') + .then(res => (res.ok ? res.json() : null)) + .then(doc => { + if (!doc) return; + setWwTimingData(doc); + refreshTimelineIfLoaded(); + }) + .catch(() => { /* no Concerto tracking */ }); + tracker = connectTracker({ onOpen: () => { console.log('Connected to WuWa Combo Tracker backend'); diff --git a/static/app/timeline.js b/static/app/timeline.js index b58ab17..8619340 100644 --- a/static/app/timeline.js +++ b/static/app/timeline.js @@ -165,7 +165,10 @@ function splitInputsTokens(str) { const out = []; let buf = ''; let paren = 0, brace = 0, bracket = 0; + let quoted = false; // inside a "Move Name" (it may hold commas) for (const ch of (str || '')) { + if (ch === '"') { quoted = !quoted; buf += ch; continue; } + if (quoted) { buf += ch; continue; } if (ch === '(') paren++; else if (ch === ')') paren = Math.max(0, paren - 1); else if (ch === '{') brace++; @@ -233,6 +236,19 @@ function parseDurationToMs(raw) { * `s` is the step data dict from the backend. * `oldSourceToken` — original combo-input token when rebuilding complex holds (hold-with-body). */ +// A step can carry the move it casts, picked from its right-click menu: +// lmb "Basic: Origin Calculus 2 (Dodge Counter)". Same as split_move_name in parser.py. +function splitMoveName(token) { + const m = String(token || '').match(/\s*"([^"]*)"\s*$/); + if (!m) return [String(token || ''), null]; + return [String(token).slice(0, m.index), m[1]]; +} + +function withMoveName(token, name) { + const base = splitMoveName(token)[0].trim(); + return name ? `${base} "${name.replace(/"/g, '')}"` : base; +} + function extractHoldWithBodyParts(oldSourceToken) { const t = (oldSourceToken || '').trim(); if (!t.toLowerCase().startsWith('hold(') || !t.endsWith(')')) return null; @@ -248,6 +264,14 @@ function extractHoldWithBodyParts(oldSourceToken) { return { key, durPart, bodyPart }; } +// -wait:Xs (alone or after a key): the next press may cut the wait short. +function markOptionalWait(el, s) { + const optionalWait = s.wait_optional || (s.type === 'wait' && s.optional); + if (!optionalWait) return; + el.classList.add('wait-optional'); + el.title = 'Optional wait: pressing the next key early is fine'; +} + function reconstructTokensForEdit(s, field, newValue, oldSourceToken) { const val = (newValue || '').trim().toLowerCase(); if (!val) return null; @@ -263,7 +287,7 @@ function reconstructTokensForEdit(s, field, newValue, oldSourceToken) { if (!key) return null; const durMs = field === 'duration' ? parseDurationToMs(val) : s.duration; if (!durMs) return null; - return [key, `wait:${formatDurationToken(durMs)}`]; + return [`${s.optional ? '-' : ''}${key}`, `${s.wait_optional ? '-' : ''}wait:${formatDurationToken(durMs)}`]; } if (s.type === 'hold') { @@ -312,7 +336,7 @@ function reconstructTokensForEdit(s, field, newValue, oldSourceToken) { // Standalone soft/hard wait: one token wait:Xs const durMs = parseDurationToMs(val); if (!durMs) return null; - return [`wait:${formatDurationToken(durMs)}`]; + return [`${s.optional ? '-' : ''}wait:${formatDurationToken(durMs)}`]; } return null; @@ -372,9 +396,10 @@ function commitStepFieldEdit(runtimeIdx, s, field, newValue) { if (!srcIndices || srcIndices.length === 0) return false; const minSrc = Math.min(...srcIndices); - const oldSourceToken = currentTokens[minSrc]; - const newTokens = reconstructTokensForEdit(s, field, newValue, oldSourceToken); + const [oldSourceToken, moveName] = splitMoveName(currentTokens[minSrc]); + const newTokens = reconstructTokensForEdit(s, field, newValue, oldSourceToken.trim()); if (!newTokens) return false; + if (moveName) newTokens[0] = withMoveName(newTokens[0], moveName); // keep a picked move // Splice: replace source token(s) at srcIndices with newTokens. const result = []; @@ -392,12 +417,17 @@ function commitStepFieldEdit(runtimeIdx, s, field, newValue) { } if (result.length === 0) return false; - const newInputs = result.join(', '); pushEditStepsUndoSnapshot(); + saveComboInputs(result.join(', ')); + return true; +} + +/** Put new inputs in the Inputs box and save them, the same way as the Save button. */ +function saveComboInputs(newInputs) { + const inputsEl = getEl('comboInputs'); + if (!inputsEl) return; inputsEl.value = newInputs; if (typeof updateComboInputHighlight === 'function') updateComboInputHighlight(); - - // Save via the same path as the Save/Update button. const toggle = getEl('stepDisplayToggle'); sendMessage('save_combo', { name: (getEl('comboName')?.value || '').toString(), @@ -411,6 +441,22 @@ function commitStepFieldEdit(runtimeIdx, s, field, newValue) { target_game: appState.targetGame, ww_team_id: appState.wwTeamId || '', }); +} + +/** + * Pick (or clear, with name null) the move for the timeline step at runtimeIdx: writes + * lmb "Basic: X 1" into its input token and saves. Returns false if it can't be mapped. + */ +function setStepMoveName(runtimeIdx, name) { + const inputsEl = getEl('comboInputs'); + if (!inputsEl) return false; + const tokens = splitInputsTokens(inputsEl.value || ''); + const src = buildRuntimeToSourceMap(tokens)[runtimeIdx]; + if (!src || !src.length) return false; + const i = Math.min(...src); + tokens[i] = withMoveName(tokens[i], name); + pushEditStepsUndoSnapshot(); + saveComboInputs(tokens.join(', ')); return true; } @@ -422,15 +468,15 @@ function buildRuntimeToSourceMap(tokens) { const srcMap = []; let i = 0; while (i < tokens.length) { - const tok = tokens[i].trim().toLowerCase(); + const tok = splitMoveName(tokens[i])[0].trim().toLowerCase(); if (!tok) { i++; continue; } // press + following soft/hard wait -> one runtime SequenceNode (press_wait tile) - if (!tok.startsWith('wait') && !tok.startsWith('hold(') && !tok.startsWith('spam(') && !tok.startsWith('[') && !tok.startsWith('{')) { + if (!tok.startsWith('wait') && !tok.startsWith('-wait') && !tok.startsWith('hold(') && !tok.startsWith('spam(') && !tok.startsWith('[') && !tok.startsWith('{')) { // Could be a plain press followed by wait:Xs if (i + 1 < tokens.length) { - const nxt = tokens[i + 1].trim().toLowerCase(); - if (nxt.startsWith('wait:')) { + const nxt = splitMoveName(tokens[i + 1])[0].trim().toLowerCase(); + if (nxt.startsWith('wait:') || nxt.startsWith('-wait:')) { srcMap.push([i, i + 1]); i += 2; continue; @@ -472,11 +518,10 @@ function buildRuntimeToSourceMap(tokens) { } /** - * Make a span inline-editable on double-click when edit mode is active. + * Make a span inline-editable on double-click. * `s` = step dict, `field` = 'key' | 'duration', `runtimeIdx` = first runtime index. */ function attachInlineEdit(span, s, field, runtimeIdx) { - if (!appState.stepEditMode) return; span.classList.add('step-field-editable'); span.title = 'Double-click to edit'; @@ -556,16 +601,47 @@ function updateTimeline(steps, opts) { }; // Names each step's move ("Zani Basic 2"); steps must be labeled in timeline order. - const labelMove = appState.showMoveNames ? createWwMoveLabeler(wwSlotNames()) : null; + // A later step can rename an earlier one (E, E, E on Augusta becomes Strike, Leap, Plunge). + // It also runs with names hidden, for the right-click "which move is this" list. + const showNames = !!appState.showMoveNames; + const labelMove = createWwMoveLabeler(wwSlotNames()); + const moveLabelEls = []; + // Moves picked from the right-click menu live in the saved inputs: lmb "Basic: X 1". + const savedTokens = splitInputsTokens(appState.savedInputs || ''); + const savedSrcMap = buildRuntimeToSourceMap(savedTokens); + // The one input token behind a tile, or -1 (a collapsed chain or group spans several). + const tokenForStep = (step) => { + const idx = Array.isArray(step && step.step_indices) ? step.step_indices : []; + const toks = new Set(idx.map((r) => (savedSrcMap[r] && savedSrcMap[r].length ? Math.min(...savedSrcMap[r]) : -1))); + return toks.size === 1 ? [...toks][0] : -1; + }; + const namedTiles = []; // tile of each named step, in labeler order (for onRevise) + labelMove.onRevise = (index, text) => { + const mc = namedTiles[index] && namedTiles[index]._moveChoice; + if (mc && mc.chosen) return; // a picked move stays + if (mc) mc.guess = mc.choices.find((n) => wwShortMoveName(n) === text) || mc.guess; + const el = moveLabelEls[index]; + if (el) { el.title = el.title.replace(el.textContent, text); el.textContent = text; } + }; function appendMoveLabel(tile, step, slot) { - if (!labelMove) return; - const text = labelMove(step, slot); - if (!text) return; + const tokIdx = tokenForStep(step); + const chosen = tokIdx >= 0 ? splitMoveName(savedTokens[tokIdx])[1] : null; + const text = labelMove(step, slot, chosen); + if (tokIdx >= 0 && text && labelMove.choices.length > 1) { + tile._moveChoice = { + runtimeIdx: step.step_indices[0], choices: labelMove.choices, inputs: labelMove.choiceInputs, + frames: labelMove.choiceFrames, chosen, guess: labelMove.choices[0], + }; + } + if (text) namedTiles.push(tile); + if (!text || !showNames) { if (text) moveLabelEls.push(null); return; } const el = document.createElement('span'); el.className = 'step-move'; el.textContent = text; - el.title = text; + const conc = slot ? labelMove.concerto(slot) : 0; + el.title = conc > 0 ? `${text}\nConcerto after this ≈ ${Math.round(conc)}/100` : text; tile.appendChild(el); + moveLabelEls.push(el); } const viewport = getEl('comboTimelineViewport'); @@ -615,28 +691,10 @@ function updateTimeline(steps, opts) { .map(v => Number.parseInt(v, 10)) .filter(v => Number.isFinite(v) && v >= 0); }; - const attachStepDeleteControl = (el, stepIndices) => { - const indices = parseStepIndices(stepIndices); - if (!appState.stepEditMode || indices.length === 0) return; - const btn = document.createElement('button'); - btn.type = 'button'; - btn.className = 'step-delete-btn'; - btn.title = 'Delete this step'; - btn.setAttribute('aria-label', 'Delete this step'); - btn.textContent = '🗑'; - btn.addEventListener('click', (ev) => { - ev.preventDefault(); - ev.stopPropagation(); - pushEditStepsUndoSnapshot(); - sendMessage('delete_timeline_step', { step_indices: indices }); - }); - el.appendChild(btn); - }; - // Drag-to-reorder: the first runtime index stored in step_indices is used as the drag handle identifier. const attachStepDragControl = (el, stepIndices) => { const indices = parseStepIndices(stepIndices); - if (!appState.stepEditMode || indices.length === 0) return; + if (indices.length === 0) return; const fromRuntimeIdx = indices[0]; el.setAttribute('draggable', 'true'); el.dataset.runtimeIdx = String(fromRuntimeIdx); @@ -731,6 +789,7 @@ function updateTimeline(steps, opts) { } if (it.optional) el.classList.add('optional'); if (it.optional && it.completed && !it.was_skipped) el.classList.add('was-pressed'); + markOptionalWait(el, it); if (it.active) el.classList.add('active'); if (it.completed) el.classList.add('completed'); @@ -802,7 +861,6 @@ function updateTimeline(steps, opts) { } }); tile.appendChild(items); - attachStepDeleteControl(tile, s.step_indices); attachStepDragControl(tile, s.step_indices); return { tile, nextActiveChar: nextChar }; } @@ -838,6 +896,7 @@ function updateTimeline(steps, opts) { itEl.className = 'step sequence-item'; if (it.optional) itEl.classList.add('optional'); if (it.optional && it.completed && !it.was_skipped) itEl.classList.add('was-pressed'); + markOptionalWait(itEl, it); if (it.active) itEl.classList.add('active'); if (it.completed) itEl.classList.add('completed'); appendStepContent(itEl, it, nextChar, ctx); @@ -845,7 +904,6 @@ function updateTimeline(steps, opts) { items.appendChild(itEl); }); tile.appendChild(items); - attachStepDeleteControl(tile, s.step_indices); attachStepDragControl(tile, s.step_indices); return { tile, nextActiveChar: nextChar }; } @@ -879,6 +937,7 @@ function updateTimeline(steps, opts) { if (s.type) tile.classList.add(s.type.replace(/_/g, '-')); if (s.optional) tile.classList.add('optional'); if (s.optional && s.completed && !s.was_skipped) tile.classList.add('was-pressed'); + markOptionalWait(tile, s); let pct = (s.progress !== undefined) ? s.progress : (s.completed ? 100 : 0); if (s.type === 'wait' || s.type === 'press_wait' || s.type === 'spam') { tile.style.setProperty('--wait-pct', `${pct}%`); @@ -899,7 +958,6 @@ function updateTimeline(steps, opts) { appendStepContent(tile, s, nextChar, ctx, runtimeIdxNormal); appendMoveLabel(tile, s, nextChar); - attachStepDeleteControl(tile, s.step_indices); attachStepDragControl(tile, s.step_indices); return { tile, nextActiveChar: nextChar }; } @@ -1322,3 +1380,121 @@ function applyAutoScroll(scrollOpts) { timeline.style.transform = `translateX(${newX}px)`; } + +// --------------------------------------------------------------------------- +// Right-click menu on Combo Steps tiles (replaces the browser's menu there only; text boxes +// keep theirs). Add entries in tileMenuItems. +// --------------------------------------------------------------------------- + +// Entries for a tile: { label, run, danger?, checked?, separator? }. +function tileMenuItems(tile, indices) { + const items = []; + // Which move this step is, when the keys alone could mean more than one (dodge into A1 vs + // Dodge Counter, Heavy 1 vs Heavy 2, ...). The pick is saved in the inputs. + const mc = tile._moveChoice; + if (mc) { + items.push({ heading: 'Which move is this?' }); + // One move is always checked: the pick, else the best guess. Picking the best guess clears + // the saved pick (it's the default anyway); clicking the checked move does nothing. + const current = (mc.chosen && mc.choices.find((n) => n.toLowerCase() === mc.chosen.toLowerCase())) || mc.guess; + mc.choices.forEach((name, i) => items.push({ + label: wwShortMoveName(name), + hint: mc.inputs[i], + frames: mc.frames && mc.frames[i], + title: name, + checked: name === current, + run: () => { + if (name === current) return; + setStepMoveName(mc.runtimeIdx, name === mc.guess ? null : name); + }, + })); + items.push({ separator: true }); + } + items.push({ + label: 'Delete step', + danger: true, + run: () => { + pushEditStepsUndoSnapshot(); + sendMessage('delete_timeline_step', { step_indices: indices }); + }, + }); + return items; +} + +let tileMenuEl = null; + +function closeTileMenu() { + if (!tileMenuEl) return; + tileMenuEl.remove(); + tileMenuEl = null; +} + +function openTileMenu(x, y, tile, indices) { + closeTileMenu(); + const menu = document.createElement('div'); + menu.className = 'ctx-menu'; + menu.setAttribute('role', 'menu'); + tileMenuItems(tile, indices).forEach((item) => { + if (item.separator) { + const hr = document.createElement('div'); + hr.className = 'ctx-menu-sep'; + menu.appendChild(hr); + return; + } + if (item.heading) { + const h = document.createElement('div'); + h.className = 'ctx-menu-heading'; + h.textContent = item.heading; + menu.appendChild(h); + return; + } + const btn = document.createElement('button'); + btn.type = 'button'; + btn.className = `ctx-menu-item${item.danger ? ' danger' : ''}${item.checked ? ' checked' : ''}`; + btn.setAttribute('role', item.checked !== undefined ? 'menuitemradio' : 'menuitem'); + if (item.checked !== undefined) btn.setAttribute('aria-checked', String(!!item.checked)); + if (item.title) btn.title = item.title; + btn.textContent = item.label; + if (item.frames) { + const f = document.createElement('span'); + f.className = 'ctx-menu-frames'; + f.textContent = item.frames; + btn.appendChild(f); + } + if (item.hint) { + const hint = document.createElement('code'); + hint.className = 'ctx-menu-hint'; + hint.textContent = item.hint; + btn.appendChild(hint); + } + btn.addEventListener('click', () => { closeTileMenu(); item.run(); }); + menu.appendChild(btn); + }); + document.body.appendChild(menu); + // Keep it on screen. + const r = menu.getBoundingClientRect(); + menu.style.left = `${Math.max(4, Math.min(x, window.innerWidth - r.width - 4))}px`; + menu.style.top = `${Math.max(4, Math.min(y, window.innerHeight - r.height - 4))}px`; + tileMenuEl = menu; + menu.querySelector('button')?.focus(); +} + +{ + const timeline = getEl('comboTimeline'); + if (timeline) { + timeline.addEventListener('contextmenu', (ev) => { + const tile = [...timeline.children].find((c) => c.contains(ev.target)); + const indices = (tile?.dataset.stepIndices || '') + .split(',').map((v) => Number.parseInt(v, 10)).filter((v) => Number.isFinite(v) && v >= 0); + if (!indices.length) return; + ev.preventDefault(); + openTileMenu(ev.clientX, ev.clientY, tile, indices); + }); + } + document.addEventListener('mousedown', (ev) => { if (tileMenuEl && !tileMenuEl.contains(ev.target)) closeTileMenu(); }, true); + document.addEventListener('keydown', (ev) => { if (ev.key === 'Escape') closeTileMenu(); }); + window.addEventListener('blur', closeTileMenu); + window.addEventListener('resize', closeTileMenu); + // wheel, not scroll: the timeline scrolls itself on updates, which shouldn't close the menu. + document.addEventListener('wheel', closeTileMenu, { passive: true }); +} diff --git a/static/characters.css b/static/characters.css index fffe076..1370a14 100644 --- a/static/characters.css +++ b/static/characters.css @@ -13,6 +13,7 @@ --line: #232933; --line-strong: #343C48; --radius: 10px; + --roster-w: 360px; --el-aero: #55d6a8; --el-electro: #b67cff; @@ -115,11 +116,9 @@ input[type="search"], select, textarea { /* --- Layout --- */ .layout { display: grid; - grid-template-columns: minmax(260px, 360px) 1fr; - gap: 20px; + grid-template-columns: var(--roster-w) 12px minmax(0, 1fr); + gap: 4px; padding: 20px; - max-width: 1400px; - margin: 0 auto; align-items: start; } @@ -163,6 +162,8 @@ input[type="search"], select, textarea { border-radius: var(--radius); padding: 20px; min-height: 300px; + min-width: 0; /* let the wide Timings table scroll inside it instead of widening the page */ + grid-column: 3; /* stay in the wide column even if the resize bar is missing (stale cached HTML) */ } .empty, .loading { color: var(--text-secondary); padding: 40px 0; text-align: center; } .error { color: var(--accent-red); padding: 20px 0; } @@ -204,15 +205,87 @@ input[type="search"], select, textarea { .move-card h3 { margin: 0 0 10px; font-size: 15px; } .move-card p { margin: 6px 0 0; } .move-card.notes p { color: #cfd5dc; } +.move-card.notes h3 .small { font-weight: 400; margin-left: 6px; } +#charNotes { + width: 100%; box-sizing: border-box; resize: vertical; min-height: 84px; + background: var(--bg-primary); color: #cfd5dc; border: 1px solid var(--line); border-radius: 6px; + padding: 8px 10px; font: inherit; font-size: 14px; line-height: 1.5; +} +#charNotes:focus { outline: none; border-color: var(--accent-blue); } .hits { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; } .hits li { display: inline-flex; align-items: center; gap: 8px; background: var(--bg-secondary); border-radius: 8px; padding: 5px 10px 5px 6px; } .keycap.hit { color: var(--accent-blue); } .entries, .follow { margin: 8px 0 0 18px; display: flex; flex-direction: column; gap: 4px; } -.moves { border-collapse: collapse; width: 100%; } -.moves td { padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: middle; } -.moves td.inp { width: 1%; white-space: nowrap; } -.other-moves { margin-top: 12px; font-size: 13px; } -.other-moves h4 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-secondary); margin: 0 0 4px; } + +/* --- Timings (WuwaLAB frame data) --- */ +.toolbar #timingFilter { width: 220px; } +.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 6px; overflow: hidden; } +.seg button { border: none; border-radius: 0; padding: 4px 10px; font-size: 12px; background: transparent; color: var(--text-secondary); } +.seg button.on { background: #152E38; color: var(--text-primary); } +.timings-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-primary); } +.timings { border-collapse: collapse; width: 100%; font-size: 13px; } +.timings th { + font-family: ui-monospace, Consolas, monospace; + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-secondary); + text-align: left; + padding: 10px 10px; + border-bottom: 1px solid var(--line); + white-space: nowrap; + cursor: help; +} +.timings th.strong { color: var(--accent-yellow); } +.timings td { padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; } +.timings .num { text-align: right; font-family: ui-monospace, Consolas, monospace; white-space: nowrap; } +.timings td.strong { font-weight: 700; color: var(--text-primary); } +.timings .zero { color: #4E5866; } +.timings .tname { min-width: 220px; } +.timings .hitf { font-family: ui-monospace, Consolas, monospace; font-size: 12px; color: var(--text-secondary); min-width: 140px; } +.timings .sep { color: #4E5866; } +.timings .tinput { white-space: nowrap; } +.timings .tinput code { font-family: ui-monospace, Consolas, monospace; font-size: 12px; color: var(--accent-blue); background: #152E38; border-radius: 4px; padding: 2px 6px; } +.timings tr.tsection td { + font-family: ui-monospace, Consolas, monospace; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.06em; + color: var(--accent-red); + padding-top: 16px; +} +.timings tbody tr:not(.tsection):not(.tstrip):hover { background: var(--bg-secondary); } +.timings tr.trow { cursor: pointer; } +.timings tr.trow.open, .timings tr.tstrip { background: var(--bg-secondary); } +.timings tr.trow.open td { border-bottom-color: transparent; } +.timings .caret { display: inline-block; width: 14px; color: var(--text-secondary); font-size: 10px; transition: transform 0.12s; } +.timings tr.trow.open .caret { transform: rotate(90deg); } +.timings tr.tstrip td { padding: 4px 24px 18px; } +.fstrip { position: relative; height: 56px; background: var(--bg-primary); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; } +.fstrip .hit { position: absolute; top: 12px; bottom: 6px; width: 2px; margin-left: -1px; background: var(--accent-blue, #4A9EFF); } +.fstrip .after-cancel { position: absolute; top: 0; bottom: 0; right: 0; background: rgba(255,255,255,0.06); } +.fstrip .zone { position: absolute; height: 12px; font-size: 9px; line-height: 12px; padding-left: 3px; font-family: ui-monospace, Consolas, monospace; overflow: hidden; } +.fstrip .zone.ms { top: 0; background: rgba(244,183,64,0.18); color: var(--accent-yellow); } +.fstrip .zone.ts { bottom: 0; background: rgba(46,197,234,0.16); color: var(--accent-blue); } +.timings .conc { color: var(--accent-blue); } +.timings .neg { color: var(--accent-red); } +.fstrip .cancel-line { position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--accent-yellow); } +.fstrip-axis { position: relative; height: 16px; margin-top: 3px; font-family: ui-monospace, Consolas, monospace; font-size: 10px; color: var(--text-secondary); } +.fstrip-axis .tick-lbl { position: absolute; transform: translateX(-50%); white-space: nowrap; } +.fstrip-axis .tick-lbl.start { transform: none; } +.fstrip-axis .tick-lbl.end { transform: translateX(-100%); } +.fstrip-axis .tick-lbl.cancel { color: var(--accent-yellow); } +.ttags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; } +.ttag { + font-family: ui-monospace, Consolas, monospace; + font-size: 10px; + letter-spacing: 0.04em; + color: var(--accent-yellow); + border: 1px solid var(--accent-yellow); + border-radius: 3px; + padding: 0 5px; +} /* --- Rotations --- */ .detail h3 { font-size: 15px; margin: 18px 0 10px; } @@ -258,9 +331,26 @@ input[type="search"], select, textarea { .as-inputs .row { display: flex; flex-wrap: wrap; gap: 6px; } .src-line { margin-top: 14px; font-size: 12px; color: var(--text-secondary); } +/* Drag handle between the roster and the detail panel (characters.js) */ +.splitter { + position: sticky; + top: 76px; + align-self: stretch; + max-height: calc(100vh - 96px); + cursor: col-resize; + display: flex; + justify-content: center; +} +.splitter::before { content: ''; width: 2px; border-radius: 1px; background: var(--line); } +.splitter:hover::before, +.resizing .splitter::before { background: var(--accent-blue); } +.resizing { cursor: col-resize; user-select: none; } + /* --- Phones / narrow windows --- */ @media (max-width: 900px) { - .layout { grid-template-columns: 1fr; padding: 12px; } + .layout { grid-template-columns: 1fr; padding: 12px; gap: 20px; } + .splitter { display: none; } + .detail { grid-column: auto; } .roster { position: static; max-height: 260px; } .skill-body { grid-template-columns: 1fr; } .team { grid-template-columns: 1fr auto; } @@ -268,6 +358,8 @@ input[type="search"], select, textarea { .topbar { padding: 10px 12px; position: static; } .topbar input[type="search"] { flex: 1; width: auto; } .who { width: 70px; } + .toolbar #timingFilter { flex: 1; width: auto; } + .timings .tname { min-width: 170px; } } /* Embedded as the main app's Characters page (characters.html?embed=1): the app shell has the title and nav. */ @@ -275,5 +367,6 @@ input[type="search"], select, textarea { .embed .topbar { position: static; background: transparent; padding: 14px 16px 0; border-bottom: 0; } .embed .topbar .back, .embed .topbar h1 { display: none; } -.embed .roster { top: 12px; max-height: calc(100vh - 24px); } +.embed .roster, +.embed .splitter { top: 12px; max-height: calc(100vh - 24px); } .embed .layout { padding: 16px; } diff --git a/static/characters.html b/static/characters.html index 149f645..6e28c33 100644 --- a/static/characters.html +++ b/static/characters.html @@ -23,6 +23,8 @@

Characters

+ +
Pick a character to see their moves and team rotations.
@@ -30,6 +32,7 @@

Characters

+ diff --git a/static/characters.js b/static/characters.js index a0c95bc..930e585 100644 --- a/static/characters.js +++ b/static/characters.js @@ -1,12 +1,14 @@ // Characters page: each character's key-input moves (static/data/ww_characters.json, drafted from -// encore.moe and checked by hand) and community team rotations (AntoCrasher's compilation + -// rotation transcripts, served by ui_server.py under /api/ww/). +// encore.moe and checked by hand), ability frame timings (static/data/ww_timings.json, from +// WuwaLAB) and community team rotations (AntoCrasher's compilation + rotation transcripts, +// served by ui_server.py under /api/ww/). // Shown inside the main app's Characters page: hide this page's own back link and title. if (new URLSearchParams(location.search).has('embed')) document.documentElement.classList.add('embed'); const ELEMENTS = ['Aero', 'Electro', 'Fusion', 'Glacio', 'Havoc', 'Spectro']; const MOVES_URL = 'data/ww_characters.json'; +const TIMINGS_URL = 'data/ww_timings.json'; // Abbreviations from AntoCrasher's rotation hub; each rotation's own list (if any) wins. const DEFAULT_GLOSSARY = { @@ -26,12 +28,25 @@ const state = { selectedId: null, tab: 'moves', moves: new Map(), // character id -> entry from ww_characters.json + timings: null, // ww_timings.json: {fps, fetched_at, characters: {id: {url, abilities}}} + timingFilter: '', + timingUnit: readPref('ww-timing-unit', 'f'), // 'f' frames or 's' seconds + openTimings: new Set(), // section|name of Timings rows whose frame strip is open transcripts: new Map(), tracker: null, + notes: null, // your notes per character id, saved in the tracker (null = not loaded yet) }; const $ = (id) => document.getElementById(id); +function readPref(key, fallback) { + try { return localStorage.getItem(key) || fallback; } catch { return fallback; } +} + +function writePref(key, value) { + try { localStorage.setItem(key, value); } catch { /* storage blocked: keep it for this visit only */ } +} + function esc(s) { return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); } @@ -102,6 +117,13 @@ function watchTracker() { state.tracker = connectTracker({ onMessage: (msg) => { // The character list rides along in the editor payload (init and later editor updates). + const notes = msg.type === 'ww_character_notes' ? msg.notes + : ((msg.editor && msg.editor.ww_character_notes) || msg.ww_character_notes); + if (notes && typeof notes === 'object') { + state.notes = { ...notes }; + // Don't swap the text out from under someone typing in it. + if (state.tab === 'moves' && document.activeElement?.id !== 'charNotes') renderDetail(); + } const chars = (msg.editor && msg.editor.ww_characters) || msg.ww_characters; if (Array.isArray(chars)) { state.mine = chars.map((c) => nameTokens(c.name || c.name_key)); @@ -180,6 +202,7 @@ function detailHeader(c) { const icon = safeUrl(c.icon); const { main, featured } = rotationsFor(c.name); const count = (main ? main.teams.length : 0) + featured.length; + const t = timingsFor(c); return `
${icon ? `` : ''}
@@ -189,6 +212,7 @@ function detailHeader(c) {
+
`; } @@ -196,7 +220,9 @@ function detailHeader(c) { function renderDetail() { const c = state.roster.find((x) => x.id === state.selectedId); if (!c) return; - const body = state.tab === 'moves' ? renderMoves(c) : renderRotations(c); + const body = state.tab === 'moves' ? renderMoves(c) + : state.tab === 'timings' ? renderTimings(c) + : renderRotations(c); $('detail').innerHTML = detailHeader(c) + body; } @@ -225,14 +251,6 @@ function renderMoves(c) { .filter(([k]) => CHAIN_AFTER[k]) .map(([k, stage]) => `
  • LMB right after ${esc(CHAIN_AFTER[k])} starts at A${stage}
  • `).join(''); - const withInput = (m.moves || []).filter((x) => x.input); - const other = (m.moves || []).filter((x) => !x.input); - const moveRows = withInput.map((x) => - `${keycap(x.input)}${esc(x.name)}${x.type ? ` ${esc(x.type)}` : ''}`).join(''); - const otherByType = {}; - other.forEach((x) => { (otherByType[x.type || 'Other'] ||= []).push(x.name); }); - const otherHtml = Object.entries(otherByType).map(([t, names]) => - `

    ${esc(t)}: ${names.map(esc).join(', ')}

    `).join(''); const follow = (m.followups || []).filter((f) => !f.basic_stage).map((f) => `
  • ${keycap(f.press)} right after ${esc(f.after)} casts ${esc(f.gives)}
  • `).join(''); @@ -245,16 +263,172 @@ function renderMoves(c) { ${hits ? `
      ${hits}
    ` : ''}

    HOLD LMB ${hold}

    ${entries ? `` : ''} + ${(m.skill_chain || []).length > 1 ? `

    Ein a row ${m.skill_chain.map(esc).join(' → ')}

    ` : ''} - ${m.notes ? `

    Notes

    ${esc(m.notes)}

    ` : ''} -
    -

    Moves

    - ${moveRows}
    - ${otherHtml ? `

    Other named moves

    ${otherHtml}
    ` : ''} -
    + ${renderNotes(c, m)} ${follow ? `

    Follow-ups

    ` : ''}`; } +// Notes: your own, saved in the tracker when you stop typing or leave the box. Until you write +// some, the box starts with the shipped notes from ww_characters.json. +function renderNotes(c, m) { + const mine = state.notes && Object.prototype.hasOwnProperty.call(state.notes, String(c.id)); + const text = mine ? state.notes[String(c.id)] : (m.notes || ''); + const live = state.tracker && state.tracker.isOpen(); + return `
    +

    Notes ${live ? '' : 'Start ComboTracker to save notes'}

    + +
    `; +} + +let notesTimer = null; +function saveNotes() { + clearTimeout(notesTimer); + const ta = $('charNotes'); + if (!ta || state.selectedId == null) return; + const id = String(state.selectedId); + const text = ta.value.trim(); + const status = $('notesStatus'); + const current = state.notes && Object.prototype.hasOwnProperty.call(state.notes, id) ? state.notes[id] : null; + if (current === text) return; + const sent = state.tracker && state.tracker.isOpen() && state.tracker.send('save_character_note', { id, text }); + if (sent === false || !state.tracker || !state.tracker.isOpen()) { + if (status) status.textContent = 'Not saved: ComboTracker isn\'t running'; + return; + } + state.notes = { ...(state.notes || {}), [id]: text }; + if (status) status.textContent = 'Saved'; +} + +// --- Timings (frame data from WuwaLAB; static/data/ww_timings.json) ------------ + +// Columns in WuwaLAB's order, timing columns only (no damage). +const TIMING_COLS = [ + { key: 'hits', label: 'Hits', tip: 'Number of hits', frames: false }, + { key: 'frames', label: 'Frames', tip: 'Full animation length', frames: true }, + { key: 'cancel', label: 'Cancel', tip: 'Earliest frame the next action can cancel this one', frames: true }, + { key: 'noswap', label: 'No swap', tip: "Frames before you can swap out", frames: true }, + { key: 'tstop', label: 'T.stop', tip: 'Time stop: the whole field freezes', frames: true }, + { key: 'mstop', label: 'M.stop', tip: 'Motion stop: hit-stop on the character', frames: true }, + { key: 'concerto', label: 'Concerto', tip: 'Concerto Energy gained (full = 100). A full bar lets the Outro fire on swap and unlocks moves like Iuno\'s Absolute Fullness', concerto: true }, + { key: 'cd', label: 'CD', tip: 'Cooldown', frames: true }, +]; + +// WuwaLAB counts Concerto in hundredths (1,000 = 10 points, -10,000 = the Outro emptying the bar). +function fmtConcerto(v) { + if (v == null) return '—'; + const pts = v / 100; + const text = Number.isInteger(pts) ? String(pts) : pts.toFixed(2).replace(/0$/, ''); + if (v === 0) return `${text}`; + return `${text}`; +} + +function timingsFor(c) { + return state.timings && state.timings.characters ? state.timings.characters[String(c.id)] : null; +} + +// 73 -> "73f" or "1.22s", per the unit toggle. Zero and missing values are dimmed. +function fmtFrames(n) { + if (n == null) return '—'; + const fps = (state.timings && state.timings.fps) || 60; + const text = state.timingUnit === 's' ? `${(n / fps).toFixed(2)}s` : `${n}f`; + return n === 0 ? `${text}` : text; +} + +function timingCell(a, col) { + const v = a[col.key]; + if (col.concerto) return fmtConcerto(v); + if (!col.frames) return v ? String(v) : `${v ?? '—'}`; + return fmtFrames(v); +} + +const timingKey = (a) => `${a.section}|${a.name}`; + +// WuwaLAB's frame strip: the animation as a bar, a tick per hit, and the part after the cancel +// frame shaded (from there the next action can cut it short). +function timingStrip(a) { + const total = a.frames || 0; + if (!total) return '

    No animation frames listed for this ability.

    '; + const pct = (f) => `${Math.min(100, Math.max(0, (f / total) * 100)).toFixed(3)}%`; + const hits = (a.hit_frames || []).filter((f) => f <= total); + const cancel = a.cancel > 0 && a.cancel < total ? a.cancel : null; + // Axis labels, most useful first; a label too close to one already placed is left off + // (its tick keeps a tooltip). + const want = [[0, '', fmtFrames(0)], [total, 'end', fmtFrames(total)]]; + if (cancel != null) want.push([cancel, 'cancel', `C ${fmtFrames(cancel)}`]); + hits.forEach((f) => want.push([f, '', fmtFrames(f)])); + const placed = []; + for (const [f, cls, text] of want) { + if (placed.some((p) => Math.abs(p[0] - f) / total < 0.06)) continue; + placed.push([f, cls, text]); + } + const marks = placed.map(([f, cls, text]) => + `${text}`); + const zones = (a.zones || []).filter((z) => z.to > z.from).map((z) => + `
    ${z.kind.toUpperCase()}
    `).join(''); + return ` +
    ${marks.join('')}
    `; +} + +function timingRow(a) { + const tags = (a.tags || []).map((t) => `${esc(t)}`).join(''); + const cells = TIMING_COLS.map((col) => + `${timingCell(a, col)}`).join(''); + const hitFrames = (a.hit_frames || []).map((f) => fmtFrames(f)).join(' · '); + const key = timingKey(a); + const open = state.openTimings.has(key); + return ` +
    ▸${esc(a.name)}
    ${tags ? `
    ${tags}
    ` : ''} + ${wwAbilityInput(a) ? `${esc(wwAbilityInput(a))}` : '—'} + ${cells} + ${hitFrames || '—'} + ${open ? `${timingStrip(a)}` : ''}`; +} + +function timingRows(t) { + const q = state.timingFilter.toLowerCase(); + const list = t.abilities.filter((a) => !q || a.name.toLowerCase().includes(q) || a.section.toLowerCase().includes(q)); + const span = TIMING_COLS.length + 3; + let html = ''; + let section = null; + for (const a of list) { + if (a.section !== section) { + section = a.section; + html += `${esc(section)}`; + } + html += timingRow(a); + } + return html || `No abilities match.`; +} + +function renderTimings(c) { + const t = timingsFor(c); + if (!state.timings) return '

    Loading timings…

    '; + if (!t) return `

    WuwaLAB has no frame data for ${esc(c.name)} yet.

    `; + const fps = state.timings.fps || 60; + const head = TIMING_COLS.map((col) => + `${esc(col.label)}`).join(''); + const unit = (u, label) => + ``; + const url = safeUrl(t.url); + return `
    + +
    ${unit('f', 'Frames')}${unit('s', 'Seconds')}
    + ${fps} fps · 1f = ${(1000 / fps).toFixed(2)} ms +
    +
    + + ${head} + ${timingRows(t)} +
    NameInputHit frames
    +
    +

    Frame data from ${url ? `WuwaLAB` : 'WuwaLAB'}${state.timings.fetched_at ? `, copied ${esc(state.timings.fetched_at.split(' ')[0])}` : ''}. Timing columns and Concerto only (Concerto is the total a move gives, in-game points). Hover a column name for what it means.

    `; +} + // --- Rotations ---------------------------------------------------------------- function teamRow(t, idx, showMain) { @@ -381,6 +555,55 @@ function saveAsCombo(teamEl) { // Events + boot // --------------------------------------------------------------------------- +// Drag the bar between the roster and the detail panel to resize the roster (remembered per +// browser); double-click resets it. +const ROSTER_WIDTH_KEY = 'ww-roster-width'; +const ROSTER_MIN = 200, ROSTER_MAX = 900; + +function setRosterWidth(px) { + const w = Math.round(Math.min(ROSTER_MAX, Math.max(ROSTER_MIN, px))); + document.documentElement.style.setProperty('--roster-w', `${w}px`); + return w; +} + +function bindResizer() { + const saved = Number(readPref(ROSTER_WIDTH_KEY, 0)); + if (saved) setRosterWidth(saved); + let bar = $('rosterResizer'); + if (!bar) { + // A stale cached characters.html may predate the bar; add it so dragging still works. + bar = document.createElement('div'); + bar.id = 'rosterResizer'; + bar.className = 'splitter'; + bar.setAttribute('role', 'separator'); + bar.setAttribute('aria-orientation', 'vertical'); + bar.setAttribute('aria-label', 'Resize character list'); + bar.title = 'Drag to resize. Double-click to reset.'; + $('roster').after(bar); + } + bar.addEventListener('pointerdown', (e) => { + e.preventDefault(); + bar.setPointerCapture(e.pointerId); + document.body.classList.add('resizing'); + const startX = e.clientX; + const startW = $('roster').getBoundingClientRect().width; + let width = startW; + const move = (ev) => { width = setRosterWidth(startW + ev.clientX - startX); }; + const up = () => { + bar.removeEventListener('pointermove', move); + document.body.classList.remove('resizing'); + writePref(ROSTER_WIDTH_KEY, String(width)); + }; + bar.addEventListener('pointermove', move); + bar.addEventListener('pointerup', up, { once: true }); + bar.addEventListener('pointercancel', up, { once: true }); + }); + bar.addEventListener('dblclick', () => { + document.documentElement.style.removeProperty('--roster-w'); + try { localStorage.removeItem(ROSTER_WIDTH_KEY); } catch { /* ignore */ } + }); +} + function bindEvents() { $('search').addEventListener('input', (e) => { state.query = e.target.value.trim(); @@ -404,6 +627,23 @@ function bindEvents() { renderDetail(); return; } + const unit = e.target.closest('[data-unit]'); + if (unit) { + state.timingUnit = unit.dataset.unit; + writePref('ww-timing-unit', state.timingUnit); + renderDetail(); + return; + } + const trow = e.target.closest('tr.trow'); + if (trow) { + const key = trow.dataset.tkey; + if (state.openTimings.has(key)) state.openTimings.delete(key); + else state.openTimings.add(key); + const c = state.roster.find((x) => x.id === state.selectedId); + const t = c && timingsFor(c); + if (t) $('timingRows').innerHTML = timingRows(t); + return; + } const act = e.target.closest('[data-act]'); if (!act) return; const teamEl = act.closest('.team'); @@ -416,6 +656,22 @@ function bindEvents() { ); } else if (act.dataset.act === 'save') saveAsCombo(teamEl); }); + // Filtering re-renders only the table body so the filter box keeps focus. + $('detail').addEventListener('input', (e) => { + if (e.target.id === 'charNotes') { + const status = $('notesStatus'); + if (status) status.textContent = 'Editing…'; + clearTimeout(notesTimer); + notesTimer = setTimeout(saveNotes, 1000); + return; + } + if (e.target.id !== 'timingFilter') return; + state.timingFilter = e.target.value.trim(); + const c = state.roster.find((x) => x.id === state.selectedId); + const t = c && timingsFor(c); + if (t) $('timingRows').innerHTML = timingRows(t); + }); + $('detail').addEventListener('focusout', (e) => { if (e.target.id === 'charNotes') saveNotes(); }); $('rawBtn').addEventListener('click', downloadRaw); window.addEventListener('hashchange', () => { const id = Number(location.hash.slice(1)); @@ -442,6 +698,16 @@ async function load() { const id = Number(location.hash.slice(1)) || state.selectedId; if (id) selectCharacter(id, { pushHash: false }); + try { + const res = await fetch(TIMINGS_URL); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + state.timings = await res.json(); + } catch (e) { + state.timings = { characters: {} }; + showNotice(`Couldn't load ability timings: ${e.message}`, 'warn'); + } + if (state.selectedId) renderDetail(); + try { const rot = await api('rotations'); state.rotations = rot.groups; @@ -474,5 +740,6 @@ async function downloadRaw() { renderFilters(); bindEvents(); +bindResizer(); watchTracker(); load(); diff --git a/static/data/ww_characters.json b/static/data/ww_characters.json index 13088ec..b6996a1 100644 --- a/static/data/ww_characters.json +++ b/static/data/ww_characters.json @@ -411,6 +411,11 @@ "chain_entry": { "heavy": 2 }, + "skill_chain": [ + "Strike", + "Leap", + "Plunge" + ], "followups": [], "notes": "With full Prowess, the Heavy becomes Thunderoar: Backstep, then press or release LMB for Spinslash and Uppercut." }, @@ -2473,7 +2478,8 @@ "Stage 3" ], "hold_lmb": "heavy", - "hold_label": "Heavy" + "hold_label": "Flux", + "hold_full_concerto": "Absolute Fullness" }, "moves": [ { diff --git a/static/data/ww_timings.json b/static/data/ww_timings.json new file mode 100644 index 0000000..4100413 --- /dev/null +++ b/static/data/ww_timings.json @@ -0,0 +1,1235 @@ +{ + "about": "Ability frame data from WuwaLAB (https://wuwalab.com): timing columns and Concerto (WuwaLAB units, 100 = 1 point; 10,000 = full), no damage. Built by tools/ww_import_timings.py; keyed by the ids in ww_characters.json.", + "source": "https://wuwalab.com", + "fetched_at": "2026-10-01 12:00", + "fps": 60, + "characters": { + "1210": { + "name": "Aemeath", + "wuwalab_name": "Aemeath", + "url": "https://wuwalab.com/characters/aemeath", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Songs Across the Universe", "tags": ["MOTION STOP 5–49F"], "genre": "INTRO", "hits": 3, "frames": 72, "cancel": 59, "noswap": 60, "tstop": 0, "mstop": 44, "cd": null, "concerto": 1000, "hit_frames": [52, 56, 59], "zones": [{"kind": "ms", "from": 5, "to": 49}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Debut of Meteoric Radiance", "tags": ["MOTION STOP 5–44F"], "genre": "INTRO", "hits": 2, "frames": 74, "cancel": 60, "noswap": 72, "tstop": 0, "mstop": 39, "cd": null, "concerto": 1000, "hit_frames": [42, 60], "zones": [{"kind": "ms", "from": 5, "to": 44}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Silent Protection", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Aemeath 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 23, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 167, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Aemeath 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 43, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [6, 11, 23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Aemeath 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 46, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 337, "hit_frames": [4, 12, 18, 25, 27], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Aemeath 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 5, "frames": 54, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 637, "hit_frames": [4, 12, 18, 25, 27], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Aemeath 4", "tags": [], "genre": "BASIC", "hits": 6, "frames": 60, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 488, "hit_frames": [0, 2, 11, 20, 29, 46], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Aemeath (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 53, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Mech 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 33, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 252, "hit_frames": [12, 20, 28], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Mech 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 47, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 334, "hit_frames": [10, 31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Mech 3", "tags": [], "genre": "BASIC", "hits": 8, "frames": 64, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 419, "hit_frames": [0, 10, 14, 19, 23, 28, 32, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Mech 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 8, "frames": 66, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 719, "hit_frames": [0, 10, 14, 19, 23, 28, 32, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Mech 4", "tags": [], "genre": "BASIC", "hits": 2, "frames": 62, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 485, "hit_frames": [38, 71], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Aemeath Charged 2 (Instant Response)", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 95, "cancel": 81, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 835, "hit_frames": [44, 49, 60, 66, 81], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Mech Charged 2 (Instant Response)", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 56, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 834, "hit_frames": [40], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Form Switch (Mech)", "tags": [], "genre": "SKILL", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sync Strike (Armament Merge)", "tags": [], "genre": "SKILL", "hits": 3, "frames": 79, "cancel": 65, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 485, "hit_frames": [6, 44, 65], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Form Switch (Aemeath)", "tags": [], "genre": "SKILL", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sync Strike (Call of Dawn)", "tags": [], "genre": "SKILL", "hits": 4, "frames": 72, "cancel": 59, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 588, "hit_frames": [10, 52, 56, 59], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Heavenfall Edict - Overdrive", "tags": ["TIME STOP 262F", "MOTION STOP 262F"], "genre": "LIBERATION", "hits": 4, "frames": 262, "cancel": 262, "noswap": 0, "tstop": 262, "mstop": 262, "cd": 1500, "concerto": 2000, "hit_frames": [214, 238, 245, 252], "zones": [{"kind": "ms", "from": 0, "to": 262}, {"kind": "ts", "from": 0, "to": 262}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Heavenfall Edict - Finale", "tags": ["TIME STOP 340F", "MOTION STOP 340F"], "genre": "LIBERATION", "hits": 1, "frames": 340, "cancel": 340, "noswap": 0, "tstop": 340, "mstop": 340, "cd": 1500, "concerto": 2000, "hit_frames": [260], "zones": [{"kind": "ms", "from": 0, "to": 340}, {"kind": "ts", "from": 0, "to": 340}]}, + {"section": "FORTE", "name": "Forte: Seraphic Duet - Overture", "tags": ["TIME STOP 99F", "MOTION STOP 99F"], "genre": "SKILL", "hits": 13, "frames": 180, "cancel": 180, "noswap": 180, "tstop": 99, "mstop": 99, "cd": null, "concerto": 1004, "hit_frames": [0, 40, 44, 48, 52, 56, 60, 120, 124, 128, 153, 159, 165], "zones": [{"kind": "ms", "from": 0, "to": 99}, {"kind": "ts", "from": 0, "to": 99}]}, + {"section": "FORTE", "name": "Forte: Seraphic Duet - Encore", "tags": ["TIME STOP 60F", "MOTION STOP 60F"], "genre": "SKILL", "hits": 8, "frames": 145, "cancel": 145, "noswap": 145, "tstop": 60, "mstop": 60, "cd": null, "concerto": 1000, "hit_frames": [0, 6, 68, 80, 89, 101, 120, 124], "zones": [{"kind": "ms", "from": 0, "to": 60}, {"kind": "ts", "from": 0, "to": 60}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill - Aemeath", "tags": ["TIME STOP 89F", "MOTION STOP 68F"], "genre": "TUNEBREAK", "hits": 5, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 89, "mstop": 68, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 68}, {"kind": "ts", "from": 0, "to": 89}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill - Mech", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 48, 74], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Rupture Response - Starburst", "tags": [], "genre": "TUNEBREAK", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []} + ] + }, + "1306": { + "name": "Augusta", + "wuwalab_name": "Augusta", + "url": "https://wuwalab.com/characters/augusta", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Stride of Goldenflare", "tags": ["MOTION STOP 6–15F"], "genre": "INTRO", "hits": 2, "frames": 73, "cancel": 63, "noswap": 68, "tstop": 0, "mstop": 9, "cd": null, "concerto": 1000, "hit_frames": [46, 63], "zones": [{"kind": "ms", "from": 6, "to": 15}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Battlesong of the Unyielding", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hunter's Path 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 26, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 145, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hunter's Path 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 51, "cancel": 33, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 338, "hit_frames": [14, 33], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hunter's Path 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 51, "cancel": 33, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 338, "hit_frames": [14, 33], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hunter's Path 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 62, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 495, "hit_frames": [14, 25, 54], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hunter's Path 4", "tags": [], "genre": "BASIC", "hits": 3, "frames": 62, "cancel": 44, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 489, "hit_frames": [37, 42, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hunter's Path (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 66, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [26, 45], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Hunter's Path", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 45, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 351, "hit_frames": [22, 31, 36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Thunderoar - Backstep", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 34, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [16], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Thunderoar - Spinslash", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 67, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 891, "hit_frames": [24, 45, 53], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 3: Thunderoar - Uppercut", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 46, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 750, "hit_frames": [6, 30], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Warrior's Blade", "tags": [], "genre": "SKILL", "hits": 3, "frames": 43, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 1000, "hit_frames": [24, 31, 43], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Undying Sunlight - Strike", "tags": [], "genre": "SKILL", "hits": 2, "frames": 48, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [27, 39], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Undying Sunlight - Leap", "tags": [], "genre": "SKILL", "hits": 3, "frames": 66, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [6, 17, 58], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Undying Sunlight - Plunge", "tags": [], "genre": "SKILL", "hits": 2, "frames": 80, "cancel": 69, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [38, 69], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation 1: Sword of Eternal Oath", "tags": [], "genre": "LIBERATION", "hits": 8, "frames": 106, "cancel": 85, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1500, "concerto": 2000, "hit_frames": [4, 10, 22, 28, 34, 62, 68, 85], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation 2: Sublime is the Sun", "tags": ["TIME STOP 503F", "MOTION STOP 503F"], "genre": "LIBERATION", "hits": 21, "frames": 503, "cancel": 503, "noswap": 0, "tstop": 503, "mstop": 503, "cd": 1500, "concerto": 2800, "hit_frames": [100, 116, 141, 161, 178, 204, 225, 241, 268, 389, 425, 435, 440, 444, 449, 454, 458, 463, 468, 472, 477], "zones": [{"kind": "ms", "from": 0, "to": 503}, {"kind": "ts", "from": 0, "to": 503}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1103": { + "name": "Baizhi", + "wuwalab_name": "Baizhi", + "url": "https://wuwalab.com/characters/baizhi", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Overflowing Frost", "tags": ["MOTION STOP 4–45F"], "genre": "INTRO", "hits": 1, "frames": 98, "cancel": 52, "noswap": 99, "tstop": 0, "mstop": 41, "cd": null, "concerto": 1000, "hit_frames": [52], "zones": [{"kind": "ms", "from": 4, "to": 45}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Rejuvinating Flow", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 30, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 385, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 43, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 463, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise 3", "tags": [], "genre": "BASIC", "hits": 7, "frames": 42, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 539, "hit_frames": [4, 6, 9, 12, 14, 17, 18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 45, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 463, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 55, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 603, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise (Swap-In)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 70, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 650, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Destined Promise (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [13], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Destined Promise", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 66, "cancel": 66, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [66], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Destined Promise (Loop)", "tags": [], "genre": "NONE", "hits": 1, "frames": 18, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 249, "hit_frames": [18], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Destined Promise (End)", "tags": [], "genre": "NONE", "hits": 0, "frames": 50, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Concentration (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "OTHER", "name": "Stimulus Feedback (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Emergency Plan", "tags": [], "genre": "SKILL", "hits": 1, "frames": 57, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 960, "concerto": 1000, "hit_frames": [26], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Momentary Union", "tags": ["TIME STOP 132F", "MOTION STOP 133F"], "genre": "LIBERATION", "hits": 0, "frames": 132, "cancel": 132, "noswap": 0, "tstop": 132, "mstop": 133, "cd": 1500, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Remnant Entities", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Remnant Entities - S4", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1307": { + "name": "Buling", + "wuwalab_name": "Buling", + "url": "https://wuwalab.com/characters/buling", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Summon and Smite", "tags": ["MOTION STOP 7–60F"], "genre": "INTRO", "hits": 1, "frames": 80, "cancel": 7, "noswap": 59, "tstop": 0, "mstop": 53, "cd": null, "concerto": 1000, "hit_frames": [61], "zones": [{"kind": "ms", "from": 7, "to": 60}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Exorcism Spell", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hexagram Calls, Lightning Falls 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 28, "cancel": 8, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 334, "hit_frames": [14, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hexagram Calls, Lightning Falls 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 45, "cancel": 2, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 540, "hit_frames": [8, 15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hexagram Calls, Lightning Falls 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 31, "cancel": 1, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 380, "hit_frames": [1, 10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hexagram Calls, Lightning Falls 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 31, "cancel": 1, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 380, "hit_frames": [1, 10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hexagram Calls, Lightning Falls 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 754, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hexagram Calls, Lightning Falls (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 46, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 496, "hit_frames": [39], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Mountain Over Thunder", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 70, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [46], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Thunder Over Mountain", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 70, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [46], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Twin Mountains", "tags": [], "genre": "HEAVY", "hits": 0, "frames": 60, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Twin Thunders", "tags": [], "genre": "HEAVY", "hits": 0, "frames": 60, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: In Shadow Thunder Stirs", "tags": [], "genre": "SKILL", "hits": 11, "frames": 53, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 2300, "hit_frames": [9, 60, 84, 108, 132, 156, 180, 204, 228, 252, 276], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Flashing Thunder Spell", "tags": ["TIME STOP 231F", "MOTION STOP 187F"], "genre": "LIBERATION", "hits": 1, "frames": 244, "cancel": 231, "noswap": 0, "tstop": 231, "mstop": 187, "cd": 1440, "concerto": 2000, "hit_frames": [187], "zones": [{"kind": "ms", "from": 0, "to": 187}, {"kind": "ts", "from": 0, "to": 231}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Flashing Thunder Spell - Harmony", "tags": ["TIME STOP 231F", "MOTION STOP 187F"], "genre": "LIBERATION", "hits": 1, "frames": 244, "cancel": 231, "noswap": 0, "tstop": 231, "mstop": 187, "cd": 1440, "concerto": 2000, "hit_frames": [187], "zones": [{"kind": "ms", "from": 0, "to": 187}, {"kind": "ts", "from": 0, "to": 231}]}, + {"section": "RESONANCE LIBERATION", "name": "S3: Summoner of Spirits, Seeker of Fate (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Five Thunders Spell Array", "tags": [], "genre": "NONE", "hits": 12, "frames": 1591, "cancel": 1591, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [271, 391, 511, 631, 751, 871, 991, 1111, 1231, 1351, 1471, 1591], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1607": { + "name": "Cantarella", + "wuwalab_name": "Cantarella", + "url": "https://wuwalab.com/characters/cantarella", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Ripple", "tags": ["MOTION STOP 4–30F"], "genre": "INTRO", "hits": 4, "frames": 76, "cancel": 54, "noswap": 80, "tstop": 0, "mstop": 26, "cd": null, "concerto": 1000, "hit_frames": [36, 42, 48, 54], "zones": [{"kind": "ms", "from": 4, "to": 30}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Tidal Surge", "tags": ["MOTION STOP 4–49F"], "genre": "INTRO", "hits": 4, "frames": 83, "cancel": 48, "noswap": 80, "tstop": 0, "mstop": 45, "cd": null, "concerto": 1000, "hit_frames": [26, 32, 35, 48], "zones": [{"kind": "ms", "from": 4, "to": 49}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Gentle Tentacles", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Illusion Collapse 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 27, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 200, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Illusion Collapse 2", "tags": [], "genre": "BASIC", "hits": 4, "frames": 45, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 368, "hit_frames": [16, 23, 29, 35], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Illusion Collapse 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 44, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 668, "hit_frames": [16, 22, 28, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Illusion Collapse 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 50, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 366, "hit_frames": [4, 19], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Illusion Collapse (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 65, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 330, "hit_frames": [10, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Phantom Sting 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 47, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 267, "hit_frames": [10, 21, 28], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Phantom Sting 1 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 39, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 267, "hit_frames": [10, 21, 28], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Phantom Sting 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 41, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 318, "hit_frames": [16, 32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Phantom Sting 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 82, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 652, "hit_frames": [19, 25, 27, 45], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Abysmal Vortex (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 65, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 330, "hit_frames": [10, 51], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Illusion Collapse", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 50, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 360, "hit_frames": [6, 18], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Delusive Dive", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 43, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 334, "hit_frames": [10, 20], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Graceful Step", "tags": [], "genre": "SKILL", "hits": 2, "frames": 38, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 1000, "hit_frames": [14, 22], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Flickering Reverie", "tags": [], "genre": "SKILL", "hits": 1, "frames": 28, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 1000, "hit_frames": [12], "zones": []}, + {"section": "FORTE", "name": "Forte: Perception Drain", "tags": [], "genre": "NONE", "hits": 2, "frames": 80, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1080, "concerto": 1200, "hit_frames": [41, 53], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Beneath the Sea", "tags": ["TIME STOP 214F", "MOTION STOP 170F"], "genre": "LIBERATION", "hits": 1, "frames": 214, "cancel": 214, "noswap": 0, "tstop": 214, "mstop": 170, "cd": 1500, "concerto": 2000, "hit_frames": [170], "zones": [{"kind": "ms", "from": 0, "to": 170}, {"kind": "ts", "from": 0, "to": 214}]}, + {"section": "RESONANCE LIBERATION", "name": "Jolt", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Dreamweaver", "tags": [], "genre": "NONE", "hits": 1, "frames": 5, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [5], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1107": { + "name": "Carlotta", + "wuwalab_name": "Carlotta", + "url": "https://wuwalab.com/characters/carlotta", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Wintertime Aria", "tags": ["MOTION STOP 84F"], "genre": "INTRO", "hits": 3, "frames": 84, "cancel": 56, "noswap": 70, "tstop": 0, "mstop": 84, "cd": null, "concerto": 1000, "hit_frames": [56, 65, 80], "zones": [{"kind": "ms", "from": 0, "to": 84}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Closing Remark", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Outro: Kaleidoscope Sparks", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Silent Execution 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 16, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 160, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Silent Execution 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 44, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 390, "hit_frames": [16, 30, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Customary Greetings", "tags": [], "genre": "BASIC", "hits": 2, "frames": 56, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 420, "hit_frames": [8, 46], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Silent Execution (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 47, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Silent Execution (Mid-Air) - Skill 2 FUA", "tags": [], "genre": "BASIC", "hits": 1, "frames": 50, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [39], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Silent Execution (Swap-In)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 56, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [10, 46], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Necessary Measures 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 24, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 195, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Necessary Measures 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 46, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 396, "hit_frames": [16, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Necessary Measures 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 80, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 690, "hit_frames": [6, 8, 12, 14, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Necessary Measures (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 50, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 715, "hit_frames": [12, 42], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Silent Execution", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 54, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 452, "hit_frames": [15, 16, 18, 19, 38], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Containment Tactics", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 54, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1500, "hit_frames": [15, 16, 18, 19, 38], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 3: Imminent Oblivion", "tags": [], "genre": "HEAVY", "hits": 6, "frames": 124, "cancel": 92, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1500, "hit_frames": [18, 25, 31, 38, 42, 92], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Art of Violence", "tags": [], "genre": "SKILL", "hits": 2, "frames": 46, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 840, "concerto": 500, "hit_frames": [8, 35], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Chromatic Splendor", "tags": [], "genre": "SKILL", "hits": 3, "frames": 121, "cancel": 82, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [40, 53, 82], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation 1: Era of New Wave", "tags": ["TIME STOP 182F", "MOTION STOP 138F"], "genre": "LIBERATION", "hits": 1, "frames": 182, "cancel": 130, "noswap": 0, "tstop": 182, "mstop": 138, "cd": 1500, "concerto": 2000, "hit_frames": [130], "zones": [{"kind": "ms", "from": 0, "to": 138}, {"kind": "ts", "from": 0, "to": 182}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation 2: Death Knell", "tags": [], "genre": "LIBERATION", "hits": 5, "frames": 70, "cancel": 52, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [40, 81, 81, 83, 83], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation 2: Death Knell - S6", "tags": [], "genre": "LIBERATION", "hits": 9, "frames": 70, "cancel": 52, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [40, 81, 81, 83, 83, 89, 89, 91, 91], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation 3: Fatal Finale", "tags": ["TIME STOP 170F", "MOTION STOP 140F"], "genre": "LIBERATION", "hits": 1, "frames": 170, "cancel": 138, "noswap": 0, "tstop": 170, "mstop": 140, "cd": null, "concerto": 1000, "hit_frames": [138], "zones": [{"kind": "ms", "from": 0, "to": 140}, {"kind": "ts", "from": 0, "to": 170}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 96F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 96, "cancel": 72, "noswap": 96, "tstop": 96, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 96}]}, + {"section": "DODGE", "name": "Dodge", "tags": [], "genre": "DODGE", "hits": 0, "frames": 25, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [], "zones": []} + ] + }, + "1409": { + "name": "Cartethyia", + "wuwalab_name": "Cartethyia", + "url": "https://wuwalab.com/characters/cartethyia", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Sword to Mark Tide's Trace", "tags": ["MOTION STOP 6–34F"], "genre": "INTRO", "hits": 4, "frames": 56, "cancel": 35, "noswap": 40, "tstop": 0, "mstop": 28, "cd": null, "concerto": 1000, "hit_frames": [35, 47, 59, 65], "zones": [{"kind": "ms", "from": 6, "to": 34}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Sword to Call for Freedom", "tags": ["MOTION STOP 6–48F"], "genre": "INTRO", "hits": 2, "frames": 71, "cancel": 61, "noswap": 56, "tstop": 0, "mstop": 42, "cd": null, "concerto": 1000, "hit_frames": [48, 61], "zones": [{"kind": "ms", "from": 6, "to": 48}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Wind's Divine Blessing", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Sword to Carve My Forms 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 20, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 98, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Sword to Carve My Forms 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 49, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 270, "hit_frames": [20, 30, 39], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Sword to Carve My Forms 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 60, "cancel": 44, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 352, "hit_frames": [8, 20, 32, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Sword to Carve My Forms 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 60, "cancel": 44, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 564, "hit_frames": [8, 20, 32, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Sword to Carve My Forms 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 62, "cancel": 11, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 311, "hit_frames": [17, 24, 31, 41], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Plunging Attack", "tags": [], "genre": "BASIC", "hits": 1, "frames": 45, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 186, "hit_frames": [32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Plunging Attack 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 45, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 186, "hit_frames": [32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Plunging Attack 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 51, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 186, "hit_frames": [33, 41, 48], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Plunging Attack 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 51, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 186, "hit_frames": [32, 42, 52], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 105, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 2", "tags": [], "genre": "BASIC", "hits": 4, "frames": 55, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 269, "hit_frames": [16, 43, 49, 55], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 59, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 315, "hit_frames": [20, 26, 30, 46], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 61, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 315, "hit_frames": [20, 28, 33, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 60, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 315, "hit_frames": [22, 31, 40, 49, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 5", "tags": [], "genre": "BASIC", "hits": 2, "frames": 50, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 278, "hit_frames": [24, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest Upward Cut", "tags": [], "genre": "BASIC", "hits": 2, "frames": 39, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 212, "hit_frames": [24, 32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 48, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 281, "hit_frames": [11, 23, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 71, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 288, "hit_frames": [16, 25, 53], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tempest 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 48, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 67, "hit_frames": [38], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Sword to Carve My Forms", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 65, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 352, "hit_frames": [29, 36, 43, 56], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Tempest 1", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 51, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 246, "hit_frames": [34, 45], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Tempest 2", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 67, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 338, "hit_frames": [37, 44, 51], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Sword to Bear Their Names", "tags": [], "genre": "SKILL", "hits": 4, "frames": 61, "cancel": 4, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 840, "concerto": 1000, "hit_frames": [4, 13, 22, 40], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Sword to Answer Waves' Call", "tags": [], "genre": "SKILL", "hits": 5, "frames": 69, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 840, "concerto": 1000, "hit_frames": [5, 11, 17, 23, 54], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: May Tempest Break the Tides", "tags": [], "genre": "SKILL", "hits": 5, "frames": 101, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [50, 56, 80, 86, 92], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: A Knight's Heartfelt Prayers", "tags": ["TIME STOP 198F", "MOTION STOP 198F"], "genre": "LIBERATION", "hits": 0, "frames": 198, "cancel": 198, "noswap": 0, "tstop": 198, "mstop": 198, "cd": 1500, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Blade of Howling Squall", "tags": ["TIME STOP 301F", "MOTION STOP 301F"], "genre": "LIBERATION", "hits": 7, "frames": 301, "cancel": 301, "noswap": 0, "tstop": 301, "mstop": 301, "cd": 1500, "concerto": 2000, "hit_frames": [253, 263, 273, 284, 294, 304, 314], "zones": [{"kind": "ms", "from": 0, "to": 301}, {"kind": "ts", "from": 0, "to": 301}]}, + {"section": "RESONANCE LIBERATION", "name": "Avatar: Fleurdelys", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 90, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Avatar: Cartethyia", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 90, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill - Cartethyia", "tags": ["TIME STOP 91F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 91, "cancel": 91, "noswap": 91, "tstop": 91, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 91}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill - Fleurdelys", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1205": { + "name": "Changli", + "wuwalab_name": "Changli", + "url": "https://wuwalab.com/characters/changli", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Obedience of Rules", "tags": ["MOTION STOP 5–44F"], "genre": "INTRO", "hits": 5, "frames": 45, "cancel": 6, "noswap": 46, "tstop": 0, "mstop": 39, "cd": null, "concerto": 1000, "hit_frames": [6, 18, 24, 29, 35], "zones": [{"kind": "ms", "from": 5, "to": 44}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Strategy of Duality", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 27, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 176, "hit_frames": [12, 21], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 32, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 210, "hit_frames": [14, 21], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 39, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 324, "hit_frames": [11, 21, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 39, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 624, "hit_frames": [11, 21, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 68, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 502, "hit_frames": [14, 32, 36, 40, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 24, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 182, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 39, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 302, "hit_frames": [17, 27], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 47, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 393, "hit_frames": [16, 25, 35], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Blazing Enlightment 4 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 53, "cancel": 6, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 377, "hit_frames": [6, 18, 24, 29, 35], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Blazing Enlightment", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 47, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 369, "hit_frames": [14, 20, 26, 35], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Blazing Enlightment (Mid-air)", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 54, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [42], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Tripartite Flames", "tags": [], "genre": "SKILL", "hits": 4, "frames": 89, "cancel": 74, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 1400, "hit_frames": [10, 18, 27, 74], "zones": []}, + {"section": "FORTE", "name": "True Sight: Conquest", "tags": [], "genre": "BASIC", "hits": 4, "frames": 73, "cancel": 68, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [26, 51, 60, 68], "zones": []}, + {"section": "FORTE", "name": "True Sight: Charge", "tags": [], "genre": "BASIC", "hits": 2, "frames": 40, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [13, 26], "zones": []}, + {"section": "FORTE", "name": "Forte: Flaming Sacrifice", "tags": [], "genre": "HEAVY", "hits": 6, "frames": 76, "cancel": 52, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [9, 15, 21, 27, 33, 52], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Radiance of Fealty", "tags": ["TIME STOP 189F", "MOTION STOP 158F"], "genre": "LIBERATION", "hits": 1, "frames": 191, "cancel": 191, "noswap": 0, "tstop": 189, "mstop": 158, "cd": 1200, "concerto": 2000, "hit_frames": [163], "zones": [{"kind": "ms", "from": 0, "to": 158}, {"kind": "ts", "from": 0, "to": 189}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1508": { + "name": "Chisa", + "wuwalab_name": "Chisa", + "url": "https://wuwalab.com/characters/chisa", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Reverberance Return", "tags": ["MOTION STOP 5–36F"], "genre": "INTRO", "hits": 1, "frames": 55, "cancel": 39, "noswap": 54, "tstop": 0, "mstop": 31, "cd": null, "concerto": 1000, "hit_frames": [39], "zones": [{"kind": "ms", "from": 5, "to": 36}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Unraveling Law Zero", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Reign of Silence 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 23, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 140, "hit_frames": [8, 17], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Reign of Silence 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 55, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 400, "hit_frames": [11, 21, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Reign of Silence 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 57, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [11, 22, 40], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Rending Lunge", "tags": [], "genre": "BASIC", "hits": 5, "frames": 80, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 637, "hit_frames": [18, 24, 27, 32, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Rending Lunge (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 84, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 637, "hit_frames": [18, 24, 27, 32, 61], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Death Snip", "tags": [], "genre": "BASIC", "hits": 3, "frames": 73, "cancel": 67, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 418, "hit_frames": [14, 54, 65], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Death Snip With Spread (Auto)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 84, "cancel": 77, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 552, "hit_frames": [14, 30, 64, 75], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Death Snip With Spread", "tags": [], "genre": "BASIC", "hits": 4, "frames": 76, "cancel": 65, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 552, "hit_frames": [15, 32, 55, 65], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thread Withdrawn", "tags": [], "genre": "BASIC", "hits": 3, "frames": 56, "cancel": 6, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 285, "hit_frames": [6, 15, 48], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Reign of Silence (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 47, "cancel": 33, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [33], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hanging Finality", "tags": [], "genre": "BASIC", "hits": 4, "frames": 77, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [6, 21, 64, 70], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Reign of Silence", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 44, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [12, 31], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Severed Facet (Mid-Air)", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 53, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 376, "hit_frames": [29, 36], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Eye of Unraveling", "tags": [], "genre": "SKILL", "hits": 1, "frames": 20, "cancel": 8, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 150, "hit_frames": [8], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Eye of Unraveling (Mid-Air)", "tags": [], "genre": "SKILL", "hits": 1, "frames": 21, "cancel": 8, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 150, "hit_frames": [8], "zones": []}, + {"section": "RESONANCE SKILL", "name": "S1: Unseen Snare Fixed DMG", "tags": [], "genre": "SKILL", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Serrated Loop", "tags": [], "genre": "SKILL", "hits": 8, "frames": 83, "cancel": 78, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 592, "hit_frames": [27, 32, 39, 41, 46, 53, 63, 78], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Serrated Loop (Half Hold)", "tags": [], "genre": "SKILL", "hits": 16, "frames": 137, "cancel": 137, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 848, "hit_frames": [27, 32, 39, 41, 46, 53, 63, 78, 92, 98, 104, 110, 116, 122, 132, 137], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Serrated Loop (Hold)", "tags": [], "genre": "SKILL", "hits": 24, "frames": 174, "cancel": 174, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1104, "hit_frames": [27, 32, 39, 41, 46, 53, 63, 78, 92, 98, 104, 110, 116, 122, 128, 132, 134, 137, 145, 149, 157, 162, 169, 174], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Moment of Nihility", "tags": ["TIME STOP 220F", "MOTION STOP 170F"], "genre": "LIBERATION", "hits": 1, "frames": 220, "cancel": 220, "noswap": 0, "tstop": 220, "mstop": 170, "cd": 1500, "concerto": 2000, "hit_frames": [160], "zones": [{"kind": "ms", "from": 0, "to": 170}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 1", "tags": [], "genre": "BASIC", "hits": 6, "frames": 31, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 198, "hit_frames": [10, 13, 19, 21, 24, 26], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2", "tags": [], "genre": "BASIC", "hits": 8, "frames": 52, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [19, 21, 23, 25, 33, 38, 39, 43], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 8, "frames": 52, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [19, 21, 23, 25, 33, 38, 39, 43], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2 (After Plunge)", "tags": [], "genre": "BASIC", "hits": 8, "frames": 43, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [11, 14, 14, 18, 25, 29, 32, 35], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2 (Hold)", "tags": [], "genre": "BASIC", "hits": 18, "frames": 84, "cancel": 73, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 540, "hit_frames": [19, 21, 23, 25, 33, 38, 39, 43, 45, 51, 52, 57, 60, 65, 66, 71, 73, 78], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2 (Hold Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 18, "frames": 84, "cancel": 73, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 540, "hit_frames": [19, 21, 23, 25, 33, 38, 39, 43, 45, 51, 52, 57, 60, 65, 66, 71, 73, 78], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2 (Hold After Plunge)", "tags": [], "genre": "BASIC", "hits": 18, "frames": 65, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 540, "hit_frames": [11, 14, 14, 18, 25, 29, 32, 35, 37, 42, 42, 47, 48, 53, 53, 57, 57, 62], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 3", "tags": [], "genre": "BASIC", "hits": 8, "frames": 67, "cancel": 56, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 360, "hit_frames": [32, 38, 41, 47, 50, 56, 62, 66], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 3 (Hold)", "tags": [], "genre": "BASIC", "hits": 14, "frames": 99, "cancel": 96, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 630, "hit_frames": [32, 38, 41, 47, 50, 56, 62, 66, 73, 77, 84, 88, 96, 99], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Eradication", "tags": [], "genre": "BASIC", "hits": 2, "frames": 156, "cancel": 65, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 4980, "hit_frames": [53, 68], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 2 Discordance", "tags": [], "genre": "BASIC", "hits": 3, "frames": 160, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 30, "hit_frames": [4, 8, 17], "zones": []}, + {"section": "FORTE", "name": "Forte: Sawring Blitz 3 Falltone", "tags": [], "genre": "BASIC", "hits": 3, "frames": 45, "cancel": 9, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 30, "hit_frames": [2, 6, 9], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1407": { + "name": "Ciaccona", + "wuwalab_name": "Ciaccona", + "url": "https://wuwalab.com/characters/ciaccona", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Roaming with the Wind", "tags": ["MOTION STOP 6–44F"], "genre": "INTRO", "hits": 1, "frames": 54, "cancel": 40, "noswap": 61, "tstop": 0, "mstop": 38, "cd": null, "concerto": 1000, "hit_frames": [40], "zones": [{"kind": "ms", "from": 6, "to": 44}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Windcalling Tunes", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 18, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 280, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 2", "tags": [], "genre": "BASIC", "hits": 4, "frames": 63, "cancel": 56, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [19, 41, 47, 56], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 42, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 648, "hit_frames": [14, 28, 34, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 42, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 648, "hit_frames": [14, 28, 28, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 90, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1200, "hit_frames": [18, 26, 34, 67], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 544, "hit_frames": [10, 26], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Quadruple Time Steps 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 43, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 480, "hit_frames": [36, 41, 48, 53], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Harmonic Allegro", "tags": [], "genre": "SKILL", "hits": 4, "frames": 39, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 1500, "hit_frames": [12, 12, 24, 36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Quadruple Time Steps", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 42, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 528, "hit_frames": [24], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Quadruple Downbeat", "tags": [], "genre": "HEAVY", "hits": 11, "frames": 75, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2500, "hit_frames": [45, 57, 69, 81, 93, 105, 117, 129, 141, 153, 165], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Improvised Symphonic Poem", "tags": ["TIME STOP 220F", "MOTION STOP 215F"], "genre": "LIBERATION", "hits": 21, "frames": 2240, "cancel": 2090, "noswap": 0, "tstop": 220, "mstop": 215, "cd": 1200, "concerto": 2000, "hit_frames": [200, 246, 326, 424, 522, 620, 718, 816, 914, 1012, 1110, 1208, 1306, 1404, 1502, 1600, 1698, 1796, 1894, 1992, 2090], "zones": [{"kind": "ms", "from": 0, "to": 215}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Select Yellow Tonic", "tags": ["TIME STOP 220F", "MOTION STOP 215F"], "genre": "LIBERATION", "hits": 21, "frames": 2240, "cancel": 2090, "noswap": 0, "tstop": 220, "mstop": 215, "cd": 1200, "concerto": 2000, "hit_frames": [200, 246, 326, 424, 522, 620, 718, 816, 914, 1012, 1110, 1208, 1306, 1404, 1502, 1600, 1698, 1796, 1894, 1992, 2090], "zones": [{"kind": "ms", "from": 0, "to": 215}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 97F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 97, "cancel": 97, "noswap": 96, "tstop": 97, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 97}]} + ] + }, + "1602": { + "name": "Danjin", + "wuwalab_name": "Danjin", + "url": "https://wuwalab.com/characters/danjin", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Vindication", "tags": ["MOTION STOP 4–51F"], "genre": "INTRO", "hits": 4, "frames": 103, "cancel": 81, "noswap": 92, "tstop": 0, "mstop": 47, "cd": null, "concerto": 1000, "hit_frames": [54, 64, 73, 81], "zones": [{"kind": "ms", "from": 4, "to": 51}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Duality", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Execution 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 16, "cancel": 9, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 108, "hit_frames": [9], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Execution 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 25, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 111, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Execution 3", "tags": [], "genre": "BASIC", "hits": 1, "frames": 28, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 150, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Execution (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 43, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 180, "hit_frames": [6, 15, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Execution (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 62, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Execution", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 40, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 210, "hit_frames": [9, 13, 17], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Carmine Gleam", "tags": [], "genre": "SKILL", "hits": 2, "frames": 29, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [12, 21], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Crimson Erosion 1", "tags": [], "genre": "SKILL", "hits": 2, "frames": 37, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [10, 18], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Crimson Erosion 2", "tags": [], "genre": "SKILL", "hits": 2, "frames": 46, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [11, 20], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Sanguine Pulse 1", "tags": [], "genre": "SKILL", "hits": 2, "frames": 33, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [8, 20], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Sanguine Pulse 2", "tags": [], "genre": "SKILL", "hits": 3, "frames": 37, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [4, 8, 24], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Sanguine Pulse 3", "tags": [], "genre": "SKILL", "hits": 3, "frames": 59, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [6, 16, 36], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Crimson Bloom", "tags": ["TIME STOP 195F", "MOTION STOP 180F"], "genre": "LIBERATION", "hits": 9, "frames": 192, "cancel": 192, "noswap": 0, "tstop": 195, "mstop": 180, "cd": 960, "concerto": 2000, "hit_frames": [74, 80, 91, 95, 102, 106, 120, 128, 169], "zones": [{"kind": "ms", "from": 0, "to": 180}, {"kind": "ts", "from": 0, "to": 192}]}, + {"section": "FORTE", "name": "Forte: Chaoscleave", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 72, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 5000, "hit_frames": [9, 17, 24, 31, 39, 46, 62], "zones": []}, + {"section": "FORTE", "name": "Forte: Scatterbloom", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 49, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [19], "zones": []}, + {"section": "FORTE", "name": "Forte: Chaoscleave (Full Energy)", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 72, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 5000, "hit_frames": [9, 17, 24, 31, 39, 46, 62], "zones": []}, + {"section": "FORTE", "name": "Forte: Scatterbloom (Full Energy)", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 49, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [19], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 91F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 91, "cancel": 91, "noswap": 90, "tstop": 91, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 91}]} + ] + }, + "1211": { + "name": "Denia", + "wuwalab_name": "Denia", + "url": "https://wuwalab.com/characters/denia", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: It's Been A While!", "tags": ["MOTION STOP 5–46F"], "genre": "INTRO", "hits": 1, "frames": 53, "cancel": 28, "noswap": 47, "tstop": 0, "mstop": 41, "cd": null, "concerto": 1000, "hit_frames": [28], "zones": [{"kind": "ms", "from": 5, "to": 46}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Knock Knock", "tags": ["MOTION STOP 5–43F"], "genre": "INTRO", "hits": 3, "frames": 81, "cancel": 49, "noswap": 75, "tstop": 0, "mstop": 38, "cd": null, "concerto": 1000, "hit_frames": [73, 80, 87], "zones": [{"kind": "ms", "from": 5, "to": 43}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Unfinished Lies", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stagecraft Form Stage 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 16, "cancel": 3, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 137, "hit_frames": [9], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stagecraft Form Stage 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 4, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 254, "hit_frames": [16, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stagecraft Form Stage 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 38, "cancel": 4, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 321, "hit_frames": [20, 29, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stagecraft Form Stage 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 38, "cancel": 4, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 621, "hit_frames": [20, 29, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stagecraft Form Stage 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 57, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 537, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stagecraft Form (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 41, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [22, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 153, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 2", "tags": [], "genre": "BASIC", "hits": 5, "frames": 52, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 394, "hit_frames": [14, 26, 32, 38, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3", "tags": [], "genre": "BASIC", "hits": 1, "frames": 36, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 36, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 4", "tags": [], "genre": "BASIC", "hits": 2, "frames": 63, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 497, "hit_frames": [14, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 1 (Empowered)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 153, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 2 (Empowered)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 52, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 394, "hit_frames": [14, 26, 32, 38, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Empowered)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 36, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Dodge Counter) (Empowered)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 36, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 4 (Empowered)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 63, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 497, "hit_frames": [14, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 153, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 52, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 394, "hit_frames": [14, 26, 32, 38, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 36, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Mid-Air Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 65, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 4 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 57, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 497, "hit_frames": [14, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 1 (Mid-Air) (Empowered)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 153, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 2 (Mid-Air) (Empowered)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 52, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 394, "hit_frames": [14, 26, 32, 38, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Mid-Air) (Empowered)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 36, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 3 (Mid-Air Dodge Counter) (Empowered)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 65, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 262, "hit_frames": [23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Forte: Breakdown Form Stage 4 (Mid-Air) (Empowered)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 57, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 497, "hit_frames": [14, 36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Stagecraft Form", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 94, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 678, "hit_frames": [42, 54], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Breakdown Form", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 74, "cancel": 63, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 575, "hit_frames": [63], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Breakdown Form (Empowered)", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 74, "cancel": 63, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 575, "hit_frames": [63], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Breakdown Form (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 48, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [22, 34], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Breakdown Form (Mid-Air) (Empowered)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 48, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [22, 34], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Phantom Bubble", "tags": [], "genre": "SKILL", "hits": 4, "frames": 64, "cancel": 5, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1200, "concerto": 2440, "hit_frames": [5, 14, 23, 32], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Beckon", "tags": [], "genre": "SKILL", "hits": 6, "frames": 60, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 240, "concerto": 436, "hit_frames": [0, 9, 24, 39, 54, 69], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Banish - Stage 1", "tags": [], "genre": "SKILL", "hits": 3, "frames": 57, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 240, "concerto": 438, "hit_frames": [28, 35, 42], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Banish - Stage 2", "tags": [], "genre": "SKILL", "hits": 1, "frames": 71, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 30, "concerto": 1470, "hit_frames": [23], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Final Act - Stagecraft Form", "tags": ["TIME STOP 251F", "MOTION STOP 251F"], "genre": "LIBERATION", "hits": 1, "frames": 259, "cancel": 259, "noswap": 0, "tstop": 251, "mstop": 251, "cd": 1500, "concerto": 2000, "hit_frames": [257], "zones": [{"kind": "ms", "from": 0, "to": 251}, {"kind": "ts", "from": 0, "to": 251}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Final Act - Breakdown Form", "tags": ["TIME STOP 131F", "MOTION STOP 131F"], "genre": "LIBERATION", "hits": 4, "frames": 171, "cancel": 171, "noswap": 160, "tstop": 131, "mstop": 131, "cd": 1500, "concerto": 2000, "hit_frames": [104, 111, 118, 125], "zones": [{"kind": "ms", "from": 0, "to": 131}, {"kind": "ts", "from": 0, "to": 131}]}, + {"section": "RESONANCE LIBERATION", "name": "Erosion Field", "tags": [], "genre": "NONE", "hits": 8, "frames": 1838, "cancel": 1838, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [158, 398, 638, 878, 1118, 1358, 1598, 1838], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill - Stagecraft Form", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill - Breakdown Form", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1203": { + "name": "Encore", + "wuwalab_name": "Encore", + "url": "https://wuwalab.com/characters/encore", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Woolies Helpers", "tags": ["MOTION STOP 56F"], "genre": "INTRO", "hits": 1, "frames": 80, "cancel": 60, "noswap": 75, "tstop": 0, "mstop": 56, "cd": null, "concerto": 1000, "hit_frames": [60], "zones": [{"kind": "ms", "from": 0, "to": 56}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Thermal Field", "tags": [], "genre": "OUTRO", "hits": 4, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0, 90, 180, 270], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wooly Attack 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 18, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 140, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wooly Attack 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 22, "cancel": 11, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 166, "hit_frames": [11], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wooly Attack 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 45, "cancel": 5, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 332, "hit_frames": [29, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wooly Attack (Swap-In)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 51, "cancel": 1, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 332, "hit_frames": [25, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wooly Attack 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 46, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 384, "hit_frames": [13, 25, 37, 49], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wooly Strike", "tags": [], "genre": "BASIC", "hits": 1, "frames": 76, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frolicking 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 29, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 266, "hit_frames": [5, 22], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frolicking 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 43, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 249, "hit_frames": [11, 23, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frolicking 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 47, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 388, "hit_frames": [14, 24, 34, 43], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frolicking 4", "tags": [], "genre": "BASIC", "hits": 3, "frames": 110, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 858, "hit_frames": [10, 50, 98], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Wooly Attack", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 56, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 470, "hit_frames": [24], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Frolicking", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 44, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 321, "hit_frames": [17], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Flaming Woolies", "tags": [], "genre": "SKILL", "hits": 8, "frames": 110, "cancel": 105, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 1500, "hit_frames": [30, 42, 52, 63, 72, 83, 95, 105], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Energetic Welcome", "tags": [], "genre": "SKILL", "hits": 1, "frames": 48, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 651, "hit_frames": [14], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Rampage", "tags": [], "genre": "SKILL", "hits": 4, "frames": 47, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 240, "concerto": 1800, "hit_frames": [35, 35, 35, 35], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Cosmos Rave", "tags": ["TIME STOP 140F", "MOTION STOP 137F"], "genre": "LIBERATION", "hits": 0, "frames": 140, "cancel": 140, "noswap": 0, "tstop": 140, "mstop": 137, "cd": 960, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "FORTE", "name": "Forte: Cloudy Frenzy", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 202, "cancel": 138, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [171], "zones": []}, + {"section": "FORTE", "name": "Forte: Cosmos Rupture", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 239, "cancel": 203, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [42, 72, 102, 132, 162, 192, 203], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1108": { + "name": "Hiyuki", + "wuwalab_name": "Hiyuki", + "url": "https://wuwalab.com/characters/hiyuki", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Frostedge - Present Self", "tags": ["MOTION STOP 4–36F"], "genre": "INTRO", "hits": 1, "frames": 64, "cancel": 42, "noswap": 60, "tstop": 0, "mstop": 32, "cd": null, "concerto": 1000, "hit_frames": [42], "zones": [{"kind": "ms", "from": 4, "to": 36}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Frostedge - Foreclaimed Self", "tags": ["MOTION STOP 5–36F"], "genre": "INTRO", "hits": 1, "frames": 68, "cancel": 42, "noswap": 60, "tstop": 0, "mstop": 31, "cd": null, "concerto": 1000, "hit_frames": [42], "zones": [{"kind": "ms", "from": 5, "to": 36}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Snowlight Blessing", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Present Self 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 32, "cancel": 25, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 244, "hit_frames": [10, 25], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Present Self 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 38, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 292, "hit_frames": [20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Present Self 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 38, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 562, "hit_frames": [20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Present Self 3", "tags": [], "genre": "BASIC", "hits": 6, "frames": 53, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 399, "hit_frames": [9, 15, 21, 27, 33, 40], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Present Self (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 52, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 415, "hit_frames": [37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 22, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 160, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 35, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 260, "hit_frames": [14, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 34, "cancel": 25, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 530, "hit_frames": [10, 25], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 74, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 545, "hit_frames": [10, 16, 22, 28, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 65, "cancel": 44, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 485, "hit_frames": [8, 16, 28, 36, 44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 5", "tags": [], "genre": "BASIC", "hits": 2, "frames": 82, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 394, "hit_frames": [17, 70], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 46, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 313, "hit_frames": [20, 30, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 50, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 340, "hit_frames": [4, 17, 32, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Foreclaimed Self 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 50, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 361, "hit_frames": [35], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Frost Splinter", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 132, "cancel": 105, "noswap": 120, "tstop": 0, "mstop": 0, "cd": null, "concerto": 999, "hit_frames": [28, 44, 105], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Foreclaimed Self", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 47, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 347, "hit_frames": [34], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Bitterfrost", "tags": ["TIME STOP 150F", "MOTION STOP 150F"], "genre": "HEAVY", "hits": 9, "frames": 150, "cancel": 150, "noswap": 0, "tstop": 150, "mstop": 150, "cd": null, "concerto": 1000, "hit_frames": [10, 17, 24, 32, 39, 46, 53, 60, 132], "zones": [{"kind": "ms", "from": 0, "to": 150}, {"kind": "ts", "from": 0, "to": 150}]}, + {"section": "RESONANCE SKILL", "name": "Skill: Present Self", "tags": [], "genre": "SKILL", "hits": 5, "frames": 85, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1200, "concerto": 637, "hit_frames": [12, 16, 20, 25, 61], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Jade Cleave", "tags": [], "genre": "SKILL", "hits": 4, "frames": 39, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 300, "hit_frames": [18, 23, 29, 34], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Petalfall (Mid-Air)", "tags": [], "genre": "SKILL", "hits": 5, "frames": 55, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 365, "hit_frames": [16, 20, 26, 29, 34], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Inward Vision", "tags": ["TIME STOP 240F", "MOTION STOP 240F"], "genre": "LIBERATION", "hits": 1, "frames": 240, "cancel": 240, "noswap": 0, "tstop": 240, "mstop": 240, "cd": 1500, "concerto": 2000, "hit_frames": [210], "zones": [{"kind": "ms", "from": 0, "to": 240}, {"kind": "ts", "from": 0, "to": 240}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Blade Liberation", "tags": ["TIME STOP 360F", "MOTION STOP 360F"], "genre": "LIBERATION", "hits": 2, "frames": 360, "cancel": 360, "noswap": 0, "tstop": 360, "mstop": 360, "cd": 1500, "concerto": 2000, "hit_frames": [216, 270], "zones": [{"kind": "ms", "from": 0, "to": 360}, {"kind": "ts", "from": 0, "to": 360}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Blade Liberation 1", "tags": ["TIME STOP 360F", "MOTION STOP 360F"], "genre": "LIBERATION", "hits": 2, "frames": 360, "cancel": 360, "noswap": 0, "tstop": 360, "mstop": 360, "cd": 1500, "concerto": 2000, "hit_frames": [216, 270], "zones": [{"kind": "ms", "from": 0, "to": 360}, {"kind": "ts", "from": 0, "to": 360}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Blade Liberation 2", "tags": ["TIME STOP 360F", "MOTION STOP 360F"], "genre": "LIBERATION", "hits": 2, "frames": 360, "cancel": 360, "noswap": 0, "tstop": 360, "mstop": 360, "cd": 1500, "concerto": 2000, "hit_frames": [216, 270], "zones": [{"kind": "ms", "from": 0, "to": 360}, {"kind": "ts", "from": 0, "to": 360}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Blade Liberation 3", "tags": ["TIME STOP 360F", "MOTION STOP 360F"], "genre": "LIBERATION", "hits": 2, "frames": 360, "cancel": 360, "noswap": 0, "tstop": 360, "mstop": 360, "cd": 1500, "concerto": 2000, "hit_frames": [216, 270], "zones": [{"kind": "ms", "from": 0, "to": 360}, {"kind": "ts", "from": 0, "to": 360}]}, + {"section": "FORTE", "name": "Forte: Stance", "tags": [], "genre": "NONE", "hits": 0, "frames": 30, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "FORTE", "name": "Forte: Iai", "tags": [], "genre": "BASIC", "hits": 5, "frames": 41, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 359, "hit_frames": [2, 15, 20, 26, 31], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1410": { + "name": "Iuno", + "wuwalab_name": "Iuno", + "url": "https://wuwalab.com/characters/iuno", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Illuminated Manifestation", "tags": ["MOTION STOP 6–32F"], "genre": "INTRO", "hits": 8, "frames": 81, "cancel": 76, "noswap": 84, "tstop": 0, "mstop": 26, "cd": null, "concerto": 1000, "hit_frames": [48, 52, 55, 59, 62, 66, 70, 76], "zones": [{"kind": "ms", "from": 6, "to": 32}]}, + {"section": "INTRO / OUTRO", "name": "Outro: From Gloom to Gleam", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moon Ring 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 29, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 123, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moon Ring 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 51, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 197, "hit_frames": [25, 32, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moon Ring 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 51, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 397, "hit_frames": [25, 32, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moon Ring 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 100, "cancel": 67, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 373, "hit_frames": [29, 56, 67], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moon Ring (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 63, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [29, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moon Ring 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 29, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 123, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moon Ring 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 51, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 197, "hit_frames": [25, 32, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moon Ring 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 51, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 397, "hit_frames": [25, 32, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moon Ring 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 100, "cancel": 67, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 373, "hit_frames": [29, 56, 67], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moon Ring (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 63, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [29, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moonbow 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 34, "cancel": 15, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 265, "hit_frames": [15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moonbow 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 45, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 351, "hit_frames": [10, 17, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moonbow 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 45, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 351, "hit_frames": [10, 17, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Moonbow 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 92, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [18, 53], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moonbow 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 34, "cancel": 15, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 665, "hit_frames": [15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moonbow 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 45, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 951, "hit_frames": [10, 17, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moonbow 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 45, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 951, "hit_frames": [10, 17, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Enhanced Moonbow 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 92, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1700, "hit_frames": [18, 53], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Flux - Moonbow", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 86, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [70], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Flux - Moonring", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 114, "cancel": 97, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 888, "hit_frames": [66, 82, 90, 97], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Absolute Fullness", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 89, "cancel": 66, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [66], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Pulse of Origins", "tags": [], "genre": "SKILL", "hits": 8, "frames": 69, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 600, "hit_frames": [28, 32, 35, 39, 42, 46, 50, 58], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Closing Refrain", "tags": [], "genre": "SKILL", "hits": 3, "frames": 109, "cancel": 82, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 480, "concerto": 800, "hit_frames": [14, 74, 82], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Unfinished Refrain", "tags": [], "genre": "SKILL", "hits": 3, "frames": 109, "cancel": 82, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 480, "concerto": 800, "hit_frames": [14, 74, 82], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Arc Beyond the Edge", "tags": [], "genre": "SKILL", "hits": 2, "frames": 85, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 800, "hit_frames": [50, 78], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Enhanced Arc Beyond the Edge", "tags": [], "genre": "SKILL", "hits": 2, "frames": 85, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 1800, "hit_frames": [50, 78], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Beneath Lunar Tides", "tags": ["TIME STOP 240F", "MOTION STOP 240F"], "genre": "LIBERATION", "hits": 1, "frames": 250, "cancel": 240, "noswap": 0, "tstop": 240, "mstop": 240, "cd": 1500, "concerto": 2000, "hit_frames": [196], "zones": [{"kind": "ms", "from": 0, "to": 240}, {"kind": "ts", "from": 0, "to": 240}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 92F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 92, "cancel": 92, "noswap": 92, "tstop": 92, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 92}]} + ] + }, + "1212": { + "name": "Jingran", + "wuwalab_name": "Jingran", + "url": "https://wuwalab.com/characters/jingran", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Question the Tombs", "tags": ["MOTION STOP 5–58F"], "genre": "INTRO", "hits": 1, "frames": 63, "cancel": 46, "noswap": 60, "tstop": 0, "mstop": 53, "cd": null, "concerto": 1000, "hit_frames": [46], "zones": [{"kind": "ms", "from": 5, "to": 58}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Rising Fortune and Ebbing Evil", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drink Soul 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 20, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 150, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drink Soul 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [10, 22], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drink Soul 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 46, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 368, "hit_frames": [12, 20, 28, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drink Soul 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 46, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 668, "hit_frames": [12, 20, 28, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drink Soul 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 81, "cancel": 55, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 516, "hit_frames": [14, 24, 51, 55], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Devil's Bane 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 20, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 134, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Devil's Bane 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 43, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 335, "hit_frames": [16, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Devil's Bane 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 67, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 536, "hit_frames": [6, 18, 43], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Devil's Bane 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 67, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 836, "hit_frames": [6, 18, 43], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Devil's Bane 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 52, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 418, "hit_frames": [6, 14, 22, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Mid-air", "tags": [], "genre": "BASIC", "hits": 1, "frames": 48, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [38], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Soul Raid", "tags": [], "genre": "HEAVY", "hits": 6, "frames": 81, "cancel": 77, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1300, "hit_frames": [16, 24, 48, 61, 69, 77], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Stardome Meander", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 90, "cancel": 80, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1300, "hit_frames": [20, 38, 67, 80], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Encroaching Yin", "tags": [], "genre": "SKILL", "hits": 4, "frames": 48, "cancel": 47, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 350, "hit_frames": [14, 24, 37, 47], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Netherworld Traverse", "tags": [], "genre": "SKILL", "hits": 7, "frames": 80, "cancel": 64, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [8, 24, 32, 64, 72, 80, 88], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Scorching Yang", "tags": [], "genre": "SKILL", "hits": 4, "frames": 47, "cancel": 47, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 350, "hit_frames": [14, 24, 37, 47], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Afterlife's Guide", "tags": [], "genre": "SKILL", "hits": 3, "frames": 79, "cancel": 71, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [12, 30, 71], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Burial of Thousand Souls", "tags": ["TIME STOP 280F", "MOTION STOP 280F"], "genre": "LIBERATION", "hits": 8, "frames": 280, "cancel": 280, "noswap": 0, "tstop": 280, "mstop": 280, "cd": 1500, "concerto": 2000, "hit_frames": [184, 196, 208, 262, 270, 280, 290, 300], "zones": [{"kind": "ms", "from": 0, "to": 280}, {"kind": "ts", "from": 0, "to": 280}]}, + {"section": "RESONANCE LIBERATION", "name": "Chimei Wangliang", "tags": [], "genre": "NONE", "hits": 1, "frames": 54, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [54], "zones": []}, + {"section": "DASH", "name": "Dodge: Shadow Step", "tags": [], "genre": "DASH", "hits": 2, "frames": 20, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [4, 16], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 80], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1304": { + "name": "Jinhsi", + "wuwalab_name": "Jinhsi", + "url": "https://wuwalab.com/characters/jinhsi", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Loong's Halo", "tags": ["MOTION STOP 4–37F"], "genre": "INTRO", "hits": 1, "frames": 60, "cancel": 49, "noswap": 78, "tstop": 0, "mstop": 33, "cd": null, "concerto": 1000, "hit_frames": [49], "zones": [{"kind": "ms", "from": 4, "to": 37}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Temporal Bender", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Outro: Temporal Bender (Unison)", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Slash of Breaking Dawn 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 28, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 248, "hit_frames": [17], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Slash of Breaking Dawn 2", "tags": [], "genre": "BASIC", "hits": 4, "frames": 45, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 365, "hit_frames": [14, 27, 34, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Slash of Breaking Dawn 3", "tags": [], "genre": "BASIC", "hits": 8, "frames": 47, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 399, "hit_frames": [15, 18, 21, 24, 27, 30, 33, 39], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Slash of Breaking Dawn 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 8, "frames": 48, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 549, "hit_frames": [15, 18, 21, 24, 27, 30, 33, 39], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Slash of Breaking Dawn 4", "tags": [], "genre": "BASIC", "hits": 2, "frames": 74, "cancel": 33, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 589, "hit_frames": [25, 33], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Slash of Breaking Dawn (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 95, "cancel": 52, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [7, 38, 52], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Incarnation 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 26, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 124, "hit_frames": [15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Incarnation 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 41, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 183, "hit_frames": [16, 25, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Incarnation 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 54, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 232, "hit_frames": [26, 31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Incarnation 4", "tags": [], "genre": "BASIC", "hits": 7, "frames": 87, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 267, "hit_frames": [0, 9, 18, 27, 36, 45, 71], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Incarnation (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 87, "cancel": 87, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 308, "hit_frames": [20, 48, 56, 71], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Slash of Breaking Dawn", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 106, "cancel": 90, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [15, 22, 29, 37, 44, 75, 90], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Incarnation", "tags": [], "genre": "BASIC", "hits": 2, "frames": 78, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 200, "hit_frames": [36, 48], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Trailing Lights of Eons", "tags": [], "genre": "SKILL", "hits": 5, "frames": 64, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 180, "concerto": 438, "hit_frames": [11, 16, 21, 26, 42], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Overflowing Radiance", "tags": [], "genre": "SKILL", "hits": 9, "frames": 84, "cancel": 74, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 400, "hit_frames": [11, 16, 21, 26, 33, 41, 49, 56, 74], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Crescent Divinity", "tags": [], "genre": "SKILL", "hits": 4, "frames": 87, "cancel": 71, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 800, "hit_frames": [20, 53, 60, 71], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Illuminous Epiphany", "tags": ["TIME STOP 132F", "MOTION STOP 174F"], "genre": "SKILL", "hits": 7, "frames": 174, "cancel": 174, "noswap": 0, "tstop": 132, "mstop": 174, "cd": null, "concerto": 2000, "hit_frames": [87, 93, 99, 105, 111, 117, 128], "zones": [{"kind": "ms", "from": 0, "to": 174}, {"kind": "ts", "from": 0, "to": 132}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Purge of Light", "tags": ["TIME STOP 231F", "MOTION STOP 231F"], "genre": "LIBERATION", "hits": 2, "frames": 231, "cancel": 231, "noswap": 0, "tstop": 231, "mstop": 231, "cd": 1440, "concerto": 2000, "hit_frames": [106, 114], "zones": [{"kind": "ms", "from": 0, "to": 231}, {"kind": "ts", "from": 0, "to": 231}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1109": { + "name": "Lucilla", + "wuwalab_name": "Lucilla", + "url": "https://wuwalab.com/characters/lucilla", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Clip It", "tags": ["MOTION STOP 6–79F"], "genre": "INTRO", "hits": 1, "frames": 81, "cancel": 38, "noswap": 82, "tstop": 0, "mstop": 73, "cd": null, "concerto": 1413, "hit_frames": [38], "zones": [{"kind": "ms", "from": 6, "to": 79}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Clip It: Hard Cut", "tags": ["MOTION STOP 5–51F"], "genre": "INTRO", "hits": 1, "frames": 52, "cancel": 27, "noswap": 52, "tstop": 0, "mstop": 46, "cd": null, "concerto": 1493, "hit_frames": [27], "zones": [{"kind": "ms", "from": 5, "to": 51}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Montage", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Snapshot 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 30, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 171, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Snapshot 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 32, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 194, "hit_frames": [14, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Snapshot 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 32, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 640, "hit_frames": [14, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Snapshot 3 - Unremarkable", "tags": [], "genre": "BASIC", "hits": 1, "frames": 86, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 574, "hit_frames": [30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Snapshot 3 - Commendable", "tags": [], "genre": "BASIC", "hits": 1, "frames": 106, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 677, "hit_frames": [50], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tracing Forms 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 27, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 207, "hit_frames": [8, 19], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tracing Forms 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 54, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 402, "hit_frames": [10, 31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tracing Forms 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 54, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 847, "hit_frames": [10, 31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tracing Forms 3 (1 Forte)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 70, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 280, "hit_frames": [34, 46], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tracing Forms 3 (2 Forte)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 106, "cancel": 82, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [34, 46, 58, 70, 82], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Tracing Forms 3 (3 Forte)", "tags": [], "genre": "BASIC", "hits": 8, "frames": 142, "cancel": 118, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1120, "hit_frames": [34, 46, 58, 70, 82, 94, 106, 118], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Letting It Go", "tags": [], "genre": "BASIC", "hits": 4, "frames": 77, "cancel": 54, "noswap": 90, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2788, "hit_frames": [0, 12, 24, 54], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Letting It Go (Echo)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 77, "cancel": 54, "noswap": 90, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2788, "hit_frames": [0, 12, 24, 54], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill Compensate", "tags": [], "genre": "SKILL", "hits": 4, "frames": 60, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 960, "concerto": 515, "hit_frames": [16, 22, 28, 53], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Spotlight", "tags": [], "genre": "SKILL", "hits": 7, "frames": 107, "cancel": 107, "noswap": 101, "tstop": 0, "mstop": 0, "cd": 960, "concerto": 2887, "hit_frames": [16, 22, 28, 57, 70, 101, 143], "zones": []}, + {"section": "FORTE", "name": "Forte: Oblivion (Chafe)", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "FORTE", "name": "Forte: Oblivion (Echo)", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Clear As Day", "tags": ["TIME STOP 3–266F", "MOTION STOP 3–266F"], "genre": "LIBERATION", "hits": 1, "frames": 266, "cancel": 266, "noswap": 0, "tstop": 263, "mstop": 263, "cd": 1500, "concerto": 2000, "hit_frames": [204], "zones": [{"kind": "ms", "from": 3, "to": 266}, {"kind": "ts", "from": 3, "to": 266}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Clear As Day (Echo)", "tags": ["TIME STOP 3–266F", "MOTION STOP 3–266F"], "genre": "LIBERATION", "hits": 1, "frames": 266, "cancel": 266, "noswap": 0, "tstop": 263, "mstop": 263, "cd": 1500, "concerto": 2000, "hit_frames": [204], "zones": [{"kind": "ms", "from": 3, "to": 266}, {"kind": "ts", "from": 3, "to": 266}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1511": { + "name": "Lucy", + "wuwalab_name": "Lucy", + "url": "https://wuwalab.com/characters/lucy", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Outdated Hallucination", "tags": ["MOTION STOP 5–32F"], "genre": "INTRO", "hits": 2, "frames": 57, "cancel": 39, "noswap": 54, "tstop": 0, "mstop": 27, "cd": null, "concerto": 1000, "hit_frames": [34, 39], "zones": [{"kind": "ms", "from": 5, "to": 32}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Countermeasure Program", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 31, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 307, "hit_frames": [14, 19], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread 1 (Hold)", "tags": [], "genre": "BASIC", "hits": 7, "frames": 80, "cancel": 66, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 617, "hit_frames": [12, 20, 28, 36, 44, 52, 63], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 37, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 307, "hit_frames": [12, 23, 26], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 69, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 606, "hit_frames": [10, 31, 53], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 70, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 996, "hit_frames": [10, 31, 54], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread 4", "tags": [], "genre": "BASIC", "hits": 6, "frames": 75, "cancel": 63, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 780, "hit_frames": [10, 12, 18, 24, 40, 63], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Locked Thread (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 97, "cancel": 79, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 586, "hit_frames": [74, 79], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thread Shredding 1", "tags": [], "genre": "BASIC", "hits": 4, "frames": 34, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 448, "hit_frames": [14, 19, 21, 23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thread Shredding 2", "tags": [], "genre": "BASIC", "hits": 5, "frames": 55, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 640, "hit_frames": [24, 28, 32, 37, 41], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thread Shredding 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 5, "frames": 55, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1120, "hit_frames": [24, 28, 33, 37, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thread Shredding 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 67, "cancel": 49, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 810, "hit_frames": [4, 6, 33, 45, 49], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thread Shredding 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 47, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 720, "hit_frames": [3, 6, 9, 13, 26], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Algorithm Compaction (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 67, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 586, "hit_frames": [34, 43], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Locked Thread 1", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 48, "cancel": 43, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 373, "hit_frames": [16, 30, 43], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Locked Thread 2", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 109, "cancel": 73, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1432, "hit_frames": [8, 17, 25, 31, 37, 56, 73], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Single Threading", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 67, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 675, "hit_frames": [8, 13, 19, 24, 38], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Dual Threading", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 67, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [10, 15, 21, 26, 48], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Multi-threading", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 61, "cancel": 55, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [38, 43, 49, 55], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Multi-threading (SQL)", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 61, "cancel": 55, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [38, 43, 49, 55], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Payload Charge", "tags": [], "genre": "SKILL", "hits": 5, "frames": 55, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 800, "hit_frames": [16, 20, 30, 42, 46], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Pulse Interference", "tags": [], "genre": "SKILL", "hits": 6, "frames": 156, "cancel": 128, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [2, 12, 114, 121, 128, 128], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Deadlock", "tags": ["TIME STOP 60F", "MOTION STOP 36F"], "genre": "SKILL", "hits": 2, "frames": 72, "cancel": 72, "noswap": 0, "tstop": 60, "mstop": 36, "cd": 840, "concerto": 800, "hit_frames": [35, 64], "zones": [{"kind": "ms", "from": 0, "to": 36}, {"kind": "ts", "from": 0, "to": 60}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Netrunner Override", "tags": ["TIME STOP 202F", "MOTION STOP 202F"], "genre": "LIBERATION", "hits": 1, "frames": 262, "cancel": 262, "noswap": 0, "tstop": 202, "mstop": 202, "cd": 1500, "concerto": 2000, "hit_frames": [251], "zones": [{"kind": "ms", "from": 0, "to": 202}, {"kind": "ts", "from": 0, "to": 202}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Old Net Deep Dive", "tags": ["TIME STOP 262F", "MOTION STOP 262F"], "genre": "LIBERATION", "hits": 1, "frames": 262, "cancel": 262, "noswap": 0, "tstop": 262, "mstop": 262, "cd": 1500, "concerto": 2000, "hit_frames": [251], "zones": [{"kind": "ms", "from": 0, "to": 262}, {"kind": "ts", "from": 0, "to": 262}]}, + {"section": "RESONANCE LIBERATION", "name": "Spoofing Program: Ping", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Spoofing Program: Synapse Burnout", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 96F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 96, "cancel": 96, "noswap": 96, "tstop": 96, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 96}]}, + {"section": "TUNE BREAK SKILL", "name": "Hack Response - Data Crash", "tags": [], "genre": "TUNEBREAK", "hits": 5, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0, 0, 0, 0, 0], "zones": []} + ] + }, + "1207": { + "name": "Lupa", + "wuwalab_name": "Lupa", + "url": "https://wuwalab.com/characters/lupa", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Try Focusing Eh?", "tags": ["MOTION STOP 6–60F"], "genre": "INTRO", "hits": 5, "frames": 70, "cancel": 34, "noswap": 60, "tstop": 0, "mstop": 54, "cd": null, "concerto": 1000, "hit_frames": [24, 34, 38, 43, 47], "zones": [{"kind": "ms", "from": 6, "to": 60}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Nowhere to Run!", "tags": ["TIME STOP 6–90F", "MOTION STOP 6–150F"], "genre": "INTRO", "hits": 5, "frames": 150, "cancel": 150, "noswap": 0, "tstop": 84, "mstop": 144, "cd": null, "concerto": 1000, "hit_frames": [114, 116, 120, 124, 129], "zones": [{"kind": "ms", "from": 6, "to": 150}, {"kind": "ts", "from": 6, "to": 90}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Stand by Me Warrior", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 42, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 268, "hit_frames": [10, 23, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 32, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 267, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 3", "tags": [], "genre": "BASIC", "hits": 7, "frames": 67, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 468, "hit_frames": [20, 24, 30, 35, 41, 46, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 89, "cancel": 73, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 730, "hit_frames": [10, 47, 66, 73], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 5, "frames": 73, "cancel": 49, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 813, "hit_frames": [15, 23, 30, 37, 49], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 30, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 227, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 56, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 461, "hit_frames": [10, 23, 27, 32, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 71, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 170, "hit_frames": [19, 50], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star Plunge", "tags": [], "genre": "BASIC", "hits": 3, "frames": 69, "cancel": 55, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 311, "hit_frames": [26, 40, 55], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Flaming Star Starfall", "tags": [], "genre": "BASIC", "hits": 5, "frames": 73, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 502, "hit_frames": [6, 13, 20, 28, 42], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Basic: Flaming Star Firestrike (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 71, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [32, 50], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Flaming Star", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 51, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 334, "hit_frames": [18, 28], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Flaming Star Wolf's Gnawing", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 50, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [26, 40], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Flaming Star Wolf's Claw", "tags": [], "genre": "HEAVY", "hits": 6, "frames": 96, "cancel": 75, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [24, 41, 46, 50, 54, 75], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Shewolf's Hunt", "tags": [], "genre": "SKILL", "hits": 1, "frames": 56, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 417, "hit_frames": [28], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Feral Fang", "tags": [], "genre": "SKILL", "hits": 1, "frames": 63, "cancel": 44, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [44], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Foebreaker", "tags": [], "genre": "SKILL", "hits": 1, "frames": 58, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2000, "hit_frames": [48], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Dance With the Wolf", "tags": [], "genre": "SKILL", "hits": 6, "frames": 151, "cancel": 102, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1502, "hit_frames": [54, 67, 73, 78, 84, 102], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Dance With the Wolf - Climax", "tags": [], "genre": "SKILL", "hits": 6, "frames": 151, "cancel": 102, "noswap": 12, "tstop": 0, "mstop": 0, "cd": null, "concerto": 3000, "hit_frames": [54, 67, 73, 78, 84, 102], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 5: Set the Arena Ablaze", "tags": [], "genre": "SKILL", "hits": 2, "frames": 96, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [57, 70], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Fire-Kissed Glory", "tags": ["TIME STOP 220F", "MOTION STOP 13–220F"], "genre": "LIBERATION", "hits": 1, "frames": 220, "cancel": 220, "noswap": 0, "tstop": 220, "mstop": 207, "cd": 1200, "concerto": 2000, "hit_frames": [168], "zones": [{"kind": "ms", "from": 13, "to": 220}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1510": { + "name": "Luuk Herssen", + "wuwalab_name": "Luuk Herssen", + "url": "https://wuwalab.com/characters/luukherssen", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Before Injection of Dawn", "tags": ["MOTION STOP 5–25F"], "genre": "INTRO", "hits": 3, "frames": 75, "cancel": 39, "noswap": 57, "tstop": 0, "mstop": 20, "cd": null, "concerto": 1000, "hit_frames": [27, 33, 39], "zones": [{"kind": "ms", "from": 5, "to": 25}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Bow to the Last Light", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 31, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [10, 21], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 56, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 445, "hit_frames": [8, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 56, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 375, "hit_frames": [50], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 3", "tags": [], "genre": "BASIC", "hits": 30, "frames": 56, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 450, "hit_frames": [33, 35, 40, 46, 52, 59, 64, 70, 76, 82, 89, 94, 100, 106, 112, 119, 124, 130, 136, 142, 149, 154, 160, 166, 172, 179, 184, 190, 196, 202], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 39, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 285, "hit_frames": [21], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 37, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 170, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 2 Scythe - Dissection (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 27, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [11, 17, 27], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 2 Scythe - Resection (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 30, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 270, "hit_frames": [13, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 3 Scythe - Dissection (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 72, "cancel": 64, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 396, "hit_frames": [8, 18, 64], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 3 Scythe - Resection (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 66, "cancel": 47, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 416, "hit_frames": [14, 47], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 3 (Mid-Air Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 69, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 750, "hit_frames": [37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Such is Light 4 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [40], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Such is Light", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 60, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 270, "hit_frames": [24], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Golden Reflux", "tags": [], "genre": "SKILL", "hits": 1, "frames": 68, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 480, "concerto": 460, "hit_frames": [46], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Aureole of Execution - Ring", "tags": [], "genre": "SKILL", "hits": 6, "frames": 81, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [15, 21, 27, 33, 39, 46], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Aureole of Execution - Ring (Aureate Judge)", "tags": [], "genre": "SKILL", "hits": 6, "frames": 81, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [15, 21, 27, 33, 39, 46], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Aureole of Execution - Breach", "tags": [], "genre": "SKILL", "hits": 3, "frames": 81, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1002, "hit_frames": [33, 39, 45], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Aureole of Execution - Breach (Aureate Judge)", "tags": [], "genre": "SKILL", "hits": 3, "frames": 81, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1002, "hit_frames": [33, 39, 45], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Aureole of Execution - Glare", "tags": [], "genre": "SKILL", "hits": 1, "frames": 106, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [50], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 4: Aureole of Execution - Glare (Aureate Judge)", "tags": [], "genre": "SKILL", "hits": 1, "frames": 106, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [50], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Basic: Golden Impale", "tags": [], "genre": "SKILL", "hits": 1, "frames": 68, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 460, "hit_frames": [46], "zones": []}, + {"section": "FORTE", "name": "Basic: Gavel of Earthshaker", "tags": [], "genre": "BASIC", "hits": 1, "frames": 41, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [26], "zones": []}, + {"section": "FORTE", "name": "Basic: Gavel of Earthshaker (Aureate Judge)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 41, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [26], "zones": []}, + {"section": "FORTE", "name": "Ichor Deposit", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "FORTE", "name": "Ichor Blade", "tags": [], "genre": "NONE", "hits": 34, "frames": 357, "cancel": 357, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [60, 69, 78, 87, 96, 105, 114, 123, 132, 141, 150, 159, 168, 177, 186, 195, 204, 213, 222, 231, 240, 249, 258, 267, 276, 285, 294, 303, 312, 321, 330, 339, 348, 357], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Rewritten in Winter's Margins", "tags": ["TIME STOP 247F", "MOTION STOP 247F"], "genre": "LIBERATION", "hits": 6, "frames": 247, "cancel": 247, "noswap": 0, "tstop": 247, "mstop": 247, "cd": 1500, "concerto": 2000, "hit_frames": [230, 232, 234, 237, 239, 242], "zones": [{"kind": "ms", "from": 0, "to": 247}, {"kind": "ts", "from": 0, "to": 247}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1509": { + "name": "Lynae", + "wuwalab_name": "Lynae", + "url": "https://wuwalab.com/characters/lynae", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Time to Show Some Colors!", "tags": ["MOTION STOP 4–66F"], "genre": "INTRO", "hits": 10, "frames": 76, "cancel": 44, "noswap": 62, "tstop": 0, "mstop": 62, "cd": null, "concerto": 2200, "hit_frames": [44, 50, 56, 62, 68, 74, 80, 86, 92, 98], "zones": [{"kind": "ms", "from": 4, "to": 66}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Let's Hit the Road!", "tags": [], "genre": "OUTRO", "hits": 22, "frames": 159, "cancel": 118, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [52, 58, 64, 70, 76, 82, 88, 94, 100, 106, 110, 112, 116, 118, 122, 128, 134, 140, 146, 152, 158, 164], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chroma Drift 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 31, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 459, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chroma Drift 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 66, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 837, "hit_frames": [15, 29, 39], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chroma Drift 3", "tags": [], "genre": "BASIC", "hits": 1, "frames": 44, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 657, "hit_frames": [28], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chroma Drift (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 49, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 738, "hit_frames": [31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chroma Drift (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 54, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 766, "hit_frames": [26, 41], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 35, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 441, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 28, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 414, "hit_frames": [10, 21], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 40, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 603, "hit_frames": [12, 16, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 70, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 794, "hit_frames": [14, 25, 38, 50], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade 5", "tags": [], "genre": "BASIC", "hits": 7, "frames": 99, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1345, "hit_frames": [8, 29, 35, 41, 47, 53, 59], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 34, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 441, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Kaleidoscopic Parade (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 54, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 766, "hit_frames": [26, 41], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Kaleidoscopic Parade (Ground Heavy)", "tags": [], "genre": "HEAVY", "hits": 8, "frames": 82, "cancel": 56, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1216, "hit_frames": [10, 14, 18, 22, 26, 30, 34, 56], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Kaleidoscopic Parade (Mid-air Heavy)", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 70, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1302, "hit_frames": [12, 20, 30, 40, 50, 60, 70], "zones": []}, + {"section": "FORTE", "name": "Basic: Spark Collision Lv.1", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 73, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 592, "hit_frames": [16, 27], "zones": []}, + {"section": "FORTE", "name": "Basic: Spark Collision Lv.2", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 109, "cancel": 63, "noswap": 36, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1776, "hit_frames": [52, 63], "zones": []}, + {"section": "FORTE", "name": "Basic: Spark Collision Lv.3", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 157, "cancel": 111, "noswap": 84, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2960, "hit_frames": [100, 111], "zones": []}, + {"section": "FORTE", "name": "Basic: Polychrome Leap 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 46, "cancel": 25, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 540, "hit_frames": [12, 21, 25], "zones": []}, + {"section": "FORTE", "name": "Basic: Polychrome Leap 2", "tags": [], "genre": "BASIC", "hits": 6, "frames": 42, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 540, "hit_frames": [12, 18, 24, 30, 36, 42], "zones": []}, + {"section": "FORTE", "name": "Basic: Polychrome Leap 3", "tags": [], "genre": "BASIC", "hits": 8, "frames": 37, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 560, "hit_frames": [14, 17, 20, 23, 50, 53, 56, 59], "zones": []}, + {"section": "FORTE", "name": "Basic: Iridescent Splash", "tags": [], "genre": "BASIC", "hits": 1, "frames": 64, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 765, "hit_frames": [38], "zones": []}, + {"section": "FORTE", "name": "Basic: Visual Impact", "tags": [], "genre": "BASIC", "hits": 1, "frames": 105, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1500, "concerto": 1458, "hit_frames": [42], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Lynae-Style Palettes", "tags": [], "genre": "SKILL", "hits": 4, "frames": 76, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 983, "hit_frames": [30, 51, 58, 62], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Additive Color", "tags": [], "genre": "SKILL", "hits": 2, "frames": 75, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 820, "hit_frames": [16, 30], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Prismatic Overblast", "tags": ["TIME STOP 240F", "MOTION STOP 223F"], "genre": "LIBERATION", "hits": 10, "frames": 240, "cancel": 240, "noswap": 0, "tstop": 240, "mstop": 223, "cd": 1500, "concerto": 2000, "hit_frames": [189, 195, 201, 207, 213, 219, 225, 231, 237, 243], "zones": [{"kind": "ms", "from": 0, "to": 223}, {"kind": "ts", "from": 0, "to": 240}]}, + {"section": "RESONANCE LIBERATION", "name": "Basic: To a Vivid Tomorrow!", "tags": [], "genre": "BASIC", "hits": 22, "frames": 159, "cancel": 118, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1942, "hit_frames": [52, 56, 60, 65, 69, 73, 77, 81, 86, 90, 94, 98, 118, 124, 130, 136, 142, 148, 154, 160, 166, 172], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 96F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 96, "cancel": 96, "noswap": 96, "tstop": 96, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 96}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Rupture Response - Spectral Analysis", "tags": [], "genre": "TUNEBREAK", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []} + ] + }, + "1209": { + "name": "Mornye", + "wuwalab_name": "Mornye", + "url": "https://wuwalab.com/characters/mornye", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Convergence", "tags": ["MOTION STOP 4–79F"], "genre": "INTRO", "hits": 6, "frames": 105, "cancel": 38, "noswap": 80, "tstop": 0, "mstop": 75, "cd": null, "concerto": 1000, "hit_frames": [38, 70, 97, 124, 151, 178], "zones": [{"kind": "ms", "from": 4, "to": 79}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Recursion", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Ground State Calibration 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 25, "cancel": 25, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 280, "hit_frames": [10, 13, 25], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Ground State Calibration 2", "tags": [], "genre": "BASIC", "hits": 6, "frames": 49, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [11, 26, 40, 49, 58, 67], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Ground State Calibration 3", "tags": [], "genre": "BASIC", "hits": 7, "frames": 49, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 520, "hit_frames": [24, 40, 49, 58, 67, 76, 85], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Ground State Calibration 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 116, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 680, "hit_frames": [51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Ground State Calibration (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 26, "cancel": 9, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 816, "hit_frames": [9], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Ground State Calibration (Mid-air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 42, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 496, "hit_frames": [36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wide Field Observation Mode 1", "tags": [], "genre": "BASIC", "hits": 4, "frames": 21, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 140, "hit_frames": [26, 34, 42, 49], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wide Field Observation Mode 2", "tags": [], "genre": "BASIC", "hits": 4, "frames": 39, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 256, "hit_frames": [17, 33, 49, 65], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wide Field Observation Mode 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 41, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 256, "hit_frames": [17, 33, 49, 65], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Wide Field Observation Mode 3", "tags": [], "genre": "BASIC", "hits": 6, "frames": 41, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 256, "hit_frames": [31, 34, 41, 62, 67, 100], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Ground State Calibration", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 95, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [19, 40, 70], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Expectation Error", "tags": [], "genre": "SKILL", "hits": 0, "frames": 21, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 300, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Optimal Solution (Parry)", "tags": [], "genre": "SKILL", "hits": 1, "frames": 110, "cancel": 44, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 904, "hit_frames": [44], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Distributed Array", "tags": [], "genre": "SKILL", "hits": 4, "frames": 60, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 960, "concerto": 1000, "hit_frames": [46, 46, 60, 60], "zones": []}, + {"section": "FORTE", "name": "Forte: Geopotential Shift", "tags": [], "genre": "HEAVY", "hits": 7, "frames": 92, "cancel": 80, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 961, "hit_frames": [22, 58, 80, 85, 112, 139, 166], "zones": []}, + {"section": "FORTE", "name": "Forte: Inversion", "tags": ["MOTION STOP 76F"], "genre": "HEAVY", "hits": 1, "frames": 76, "cancel": 76, "noswap": 76, "tstop": 0, "mstop": 76, "cd": null, "concerto": 1196, "hit_frames": [76], "zones": [{"kind": "ms", "from": 0, "to": 76}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Critical Protocol", "tags": ["TIME STOP 300F", "MOTION STOP 300F"], "genre": "LIBERATION", "hits": 1, "frames": 300, "cancel": 300, "noswap": 0, "tstop": 300, "mstop": 300, "cd": 1500, "concerto": 2000, "hit_frames": [272], "zones": [{"kind": "ms", "from": 0, "to": 300}, {"kind": "ts", "from": 0, "to": 300}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Rupture Response - Particle Jet", "tags": [], "genre": "TUNEBREAK", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "OTHER", "name": "Syntony Field (Heal)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "OTHER", "name": "High Syntony Field (Heal)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []} + ] + }, + "1204": { + "name": "Mortefi", + "wuwalab_name": "Mortefi", + "url": "https://wuwalab.com/characters/mortefi", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Dissonance", "tags": ["MOTION STOP 4–49F"], "genre": "INTRO", "hits": 1, "frames": 90, "cancel": 44, "noswap": 90, "tstop": 0, "mstop": 45, "cd": null, "concerto": 1000, "hit_frames": [44], "zones": [{"kind": "ms", "from": 4, "to": 49}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Rage Transposition", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 277, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 35, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 468, "hit_frames": [10, 18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show 3", "tags": [], "genre": "BASIC", "hits": 1, "frames": 40, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 616, "hit_frames": [24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 91, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1209, "hit_frames": [18, 24, 30, 36, 54], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 48, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 640, "hit_frames": [32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show 1 (Mid-Air)", "tags": [], "genre": "NONE", "hits": 1, "frames": 11, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Impromptu Show 2 (Mid-Air)", "tags": [], "genre": "NONE", "hits": 1, "frames": 11, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Passionate Variation", "tags": [], "genre": "SKILL", "hits": 1, "frames": 46, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 840, "concerto": 1800, "hit_frames": [16], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Fury Fugue", "tags": [], "genre": "SKILL", "hits": 1, "frames": 67, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1800, "hit_frames": [20], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Violent Finale", "tags": ["TIME STOP 120F", "MOTION STOP 64F"], "genre": "LIBERATION", "hits": 1, "frames": 120, "cancel": 120, "noswap": 0, "tstop": 120, "mstop": 64, "cd": 1200, "concerto": 2000, "hit_frames": [62], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 120}]}, + {"section": "RESONANCE LIBERATION", "name": "Marcato", "tags": [], "genre": "NONE", "hits": 1, "frames": 32, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [32], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Marcato - S1", "tags": [], "genre": "NONE", "hits": 2, "frames": 21, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [21, 21], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Marcato - S5", "tags": [], "genre": "NONE", "hits": 4, "frames": 45, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [39, 41, 45, 45], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 96F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 96, "cancel": 96, "noswap": 96, "tstop": 96, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 96}]} + ] + }, + "1506": { + "name": "Phoebe", + "wuwalab_name": "Phoebe", + "url": "https://wuwalab.com/characters/phoebe", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Golden Grace", "tags": ["MOTION STOP 4–46F"], "genre": "INTRO", "hits": 1, "frames": 98, "cancel": 45, "noswap": 69, "tstop": 0, "mstop": 42, "cd": null, "concerto": 1000, "hit_frames": [45], "zones": [{"kind": "ms", "from": 4, "to": 46}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Attentive Heart", "tags": [], "genre": "OUTRO", "hits": 8, "frames": 180, "cancel": 135, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [30, 45, 60, 75, 90, 105, 120, 135], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chamuel's Star 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 22, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 199, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chamuel's Star 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 32, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 268, "hit_frames": [12, 120], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chamuel's Star 3", "tags": [], "genre": "BASIC", "hits": 6, "frames": 61, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 582, "hit_frames": [36, 42, 48, 54, 60, 66], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Chamuel's Star 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 6, "frames": 60, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 882, "hit_frames": [36, 42, 48, 54, 60, 66], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: O Come Divine Light 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 26, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 199, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: O Come Divine Light 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 267, "hit_frames": [16, 26], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: O Come Divine Light 3", "tags": [], "genre": "BASIC", "hits": 8, "frames": 60, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 584, "hit_frames": [36, 42, 48, 54, 60, 66, 72, 78], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: O Come Divine Light 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 8, "frames": 60, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 888, "hit_frames": [36, 42, 48, 54, 60, 66, 72, 78], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: O Come Divine Light (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 78, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [2, 27], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: To Where Light Shines", "tags": [], "genre": "SKILL", "hits": 2, "frames": 70, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 228, "hit_frames": [37, 44], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: To Where Light Shines Teleport", "tags": [], "genre": "SKILL", "hits": 2, "frames": 91, "cancel": 53, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 42, "concerto": 228, "hit_frames": [53, 59], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Ring of Mirrors: Refracted Holy Light", "tags": [], "genre": "NONE", "hits": 2, "frames": 18, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [6, 18], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Utter Confession", "tags": [], "genre": "SKILL", "hits": 1, "frames": 66, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 4000, "hit_frames": [48], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Absolution Litany", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 66, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [48], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Starflash", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 55, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 414, "hit_frames": [28, 34, 40], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Starflash (Confession) - S3", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 55, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 414, "hit_frames": [28, 34, 40], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: O Come Divine Light", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 61, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 556, "hit_frames": [40, 49, 58, 67], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Dawn of Enlightenment", "tags": ["TIME STOP 220F", "MOTION STOP 218F"], "genre": "LIBERATION", "hits": 1, "frames": 220, "cancel": 220, "noswap": 0, "tstop": 220, "mstop": 218, "cd": 1500, "concerto": 2000, "hit_frames": [186], "zones": [{"kind": "ms", "from": 0, "to": 218}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Dawn of Enlightenment (Absolution)", "tags": ["TIME STOP 220F", "MOTION STOP 218F"], "genre": "LIBERATION", "hits": 1, "frames": 220, "cancel": 220, "noswap": 0, "tstop": 220, "mstop": 218, "cd": 1500, "concerto": 2000, "hit_frames": [186], "zones": [{"kind": "ms", "from": 0, "to": 218}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Dawn of Enlightenment (Confession) - S1", "tags": ["TIME STOP 220F", "MOTION STOP 218F"], "genre": "LIBERATION", "hits": 1, "frames": 220, "cancel": 220, "noswap": 0, "tstop": 220, "mstop": 218, "cd": 1500, "concerto": 2000, "hit_frames": [186], "zones": [{"kind": "ms", "from": 0, "to": 218}, {"kind": "ts", "from": 0, "to": 220}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1608": { + "name": "Phrolova", + "wuwalab_name": "Phrolova", + "url": "https://wuwalab.com/characters/phrolova", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Suite of Quietus", "tags": ["MOTION STOP 6–38F"], "genre": "INTRO", "hits": 2, "frames": 80, "cancel": 59, "noswap": 76, "tstop": 0, "mstop": 32, "cd": null, "concerto": 1000, "hit_frames": [43, 62], "zones": [{"kind": "ms", "from": 6, "to": 38}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Suite of Immortality", "tags": ["MOTION STOP 6–56F"], "genre": "INTRO", "hits": 1, "frames": 93, "cancel": 60, "noswap": 90, "tstop": 0, "mstop": 50, "cd": null, "concerto": 1000, "hit_frames": [60], "zones": [{"kind": "ms", "from": 6, "to": 56}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Unfinished Piece", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Life and Death 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 44, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 336, "hit_frames": [18, 35], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Life and Death 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 36, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Life and Death 2 (Backstep)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 50, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Life and Death 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 50, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 384, "hit_frames": [24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Life and Death 3", "tags": [], "genre": "BASIC", "hits": 6, "frames": 81, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 618, "hit_frames": [30, 36, 42, 48, 54, 60], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Life and Death (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 63, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 400, "hit_frames": [48], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Movement of Fate and Finality", "tags": [], "genre": "BASIC", "hits": 7, "frames": 92, "cancel": 2, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1002, "hit_frames": [8, 14, 20, 26, 53, 60, 67], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hecate 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 40, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Hecate 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 49, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [4, 22], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Movement of Life and Death", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 62, "cancel": 49, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 502, "hit_frames": [26, 49], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Scarlet Coda", "tags": [], "genre": "HEAVY", "hits": 11, "frames": 172, "cancel": 139, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1500, "concerto": 4000, "hit_frames": [14, 41, 59, 66, 73, 80, 87, 95, 102, 109, 139], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Whispers in a Fleeting Dream", "tags": [], "genre": "SKILL", "hits": 2, "frames": 34, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 1000, "hit_frames": [10, 48], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Murmurs in a Haunting Dream", "tags": [], "genre": "SKILL", "hits": 6, "frames": 79, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [10, 16, 22, 28, 38, 81], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Waltz of Forsaken Depths", "tags": ["TIME STOP 240F", "MOTION STOP 240F"], "genre": "LIBERATION", "hits": 0, "frames": 240, "cancel": 240, "noswap": 0, "tstop": 240, "mstop": 240, "cd": null, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Curtain Call", "tags": [], "genre": "LIBERATION", "hits": 1, "frames": 83, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 585, "hit_frames": [42], "zones": []}, + {"section": "FORTE", "name": "Basic: Enhanced Hecate", "tags": [], "genre": "BASIC", "hits": 0, "frames": 56, "cancel": 56, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "FORTE", "name": "Basic: Hecate - Strings", "tags": [], "genre": "BASIC", "hits": 2, "frames": 91, "cancel": 91, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [26, 56], "zones": []}, + {"section": "FORTE", "name": "Basic: Hecate - Winds", "tags": [], "genre": "BASIC", "hits": 2, "frames": 70, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [26, 56], "zones": []}, + {"section": "FORTE", "name": "Basic: Hecate - Cadenza", "tags": [], "genre": "BASIC", "hits": 2, "frames": 70, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [26, 56], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1413": { + "name": "Qingxiao", + "wuwalab_name": "Qingxiao", + "url": "https://wuwalab.com/characters/qingxiao", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Tonality Shift", "tags": ["MOTION STOP 3–31F"], "genre": "INTRO", "hits": 3, "frames": 64, "cancel": 52, "noswap": 57, "tstop": 0, "mstop": 28, "cd": null, "concerto": 1000, "hit_frames": [35, 43, 52], "zones": [{"kind": "ms", "from": 3, "to": 31}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Lingering Song", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 29, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 218, "hit_frames": [15, 22], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 1 (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 29, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 218, "hit_frames": [15, 22], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 37, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 268, "hit_frames": [12, 28], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 2 (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 2, "frames": 37, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 268, "hit_frames": [12, 28], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 48, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 352, "hit_frames": [6, 12, 18, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 3 (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 48, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 352, "hit_frames": [6, 12, 18, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 3 (Dodge Counter)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 48, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 652, "hit_frames": [6, 12, 18, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 3 (Dodge Counter) (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 48, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 652, "hit_frames": [6, 12, 18, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 52, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 392, "hit_frames": [17, 32, 36, 39, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 4 (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 52, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 392, "hit_frames": [17, 32, 36, 39, 42], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 6, "frames": 42, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 325, "hit_frames": [0, 6, 12, 18, 24, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 1 (Mid-Air) (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 6, "frames": 42, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 325, "hit_frames": [0, 6, 12, 18, 24, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 45, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 324, "hit_frames": [12, 18, 23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 2 (Mid-Air) (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 45, "cancel": 23, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 324, "hit_frames": [12, 18, 23], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 6, "frames": 86, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [18, 24, 30, 36, 42, 70], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Stringblade 3 (Mid-Air) (Heaven's Clarity)", "tags": [], "genre": "BASIC", "hits": 6, "frames": 86, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [18, 24, 30, 36, 42, 70], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Plunging Attack", "tags": [], "genre": "BASIC", "hits": 1, "frames": 63, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [39], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Stringblade", "tags": [], "genre": "HEAVY", "hits": 10, "frames": 137, "cancel": 109, "noswap": 126, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1053, "hit_frames": [26, 35, 43, 44, 52, 54, 61, 63, 72, 109], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 40, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 324, "hit_frames": [12, 16, 19], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 1 (Enhanced)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 40, "cancel": 19, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 324, "hit_frames": [12, 16, 19], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 2", "tags": [], "genre": "BASIC", "hits": 5, "frames": 58, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 415, "hit_frames": [14, 17, 20, 26, 29], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 2 (Enhanced)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 58, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 415, "hit_frames": [14, 17, 20, 26, 29], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 57, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 451, "hit_frames": [10, 20, 30, 40, 50], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 3 (Enhanced)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 57, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 451, "hit_frames": [10, 20, 30, 40, 50], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 4", "tags": [], "genre": "BASIC", "hits": 5, "frames": 87, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 650, "hit_frames": [10, 23, 32, 43, 61], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 4 (Enhanced)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 87, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 650, "hit_frames": [10, 23, 32, 43, 61], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 4 (Dodge Counter)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 87, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 950, "hit_frames": [10, 23, 32, 43, 61], "zones": []}, + {"section": "FORTE", "name": "Basic: Ephemeral Transcendence 4 (Enhanced Dodge Counter)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 87, "cancel": 61, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 950, "hit_frames": [10, 23, 32, 43, 61], "zones": []}, + {"section": "FORTE", "name": "Forte: Heaven's Reckoning - Ephemeral Transcendence", "tags": ["TIME STOP 180F", "MOTION STOP 180F"], "genre": "HEAVY", "hits": 10, "frames": 180, "cancel": 180, "noswap": 180, "tstop": 180, "mstop": 180, "cd": null, "concerto": 2500, "hit_frames": [32, 44, 47, 58, 60, 72, 83, 90, 102, 119], "zones": [{"kind": "ms", "from": 0, "to": 180}, {"kind": "ts", "from": 0, "to": 180}]}, + {"section": "FORTE", "name": "Forte: Heaven's Reckoning - Ephemeral Transcendence (Enhanced)", "tags": ["TIME STOP 180F", "MOTION STOP 180F"], "genre": "HEAVY", "hits": 10, "frames": 180, "cancel": 180, "noswap": 180, "tstop": 180, "mstop": 180, "cd": null, "concerto": 2500, "hit_frames": [32, 44, 47, 58, 60, 72, 83, 90, 102, 119], "zones": [{"kind": "ms", "from": 0, "to": 180}, {"kind": "ts", "from": 0, "to": 180}]}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Severing Note - Judgement", "tags": [], "genre": "SKILL", "hits": 3, "frames": 81, "cancel": 80, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 1200, "concerto": 500, "hit_frames": [4, 13, 72], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Severing Note - Ascendant", "tags": [], "genre": "SKILL", "hits": 3, "frames": 64, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 340, "hit_frames": [16, 16, 25], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Severing Note - Ascendant (Heaven's Clarity)", "tags": [], "genre": "SKILL", "hits": 3, "frames": 64, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 340, "hit_frames": [16, 16, 25], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Billows Beneath Heaven", "tags": ["TIME STOP 300F", "MOTION STOP 300F"], "genre": "LIBERATION", "hits": 11, "frames": 300, "cancel": 300, "noswap": 0, "tstop": 300, "mstop": 300, "cd": 1500, "concerto": 2000, "hit_frames": [140, 146, 152, 158, 164, 170, 176, 182, 188, 194, 238], "zones": [{"kind": "ms", "from": 0, "to": 300}, {"kind": "ts", "from": 0, "to": 300}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 120F", "MOTION STOP 100F"], "genre": "TUNEBREAK", "hits": 5, "frames": 120, "cancel": 120, "noswap": 120, "tstop": 120, "mstop": 100, "cd": null, "concerto": 0, "hit_frames": [64, 70, 76, 82, 101], "zones": [{"kind": "ms", "from": 0, "to": 100}, {"kind": "ts", "from": 0, "to": 120}]} + ] + }, + "1411": { + "name": "Qiuyuan", + "wuwalab_name": "Qiuyuan", + "url": "https://wuwalab.com/characters/qiuyuan", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Attack the Must-Defend", "tags": ["MOTION STOP 6–54F"], "genre": "INTRO", "hits": 7, "frames": 74, "cancel": 68, "noswap": 70, "tstop": 0, "mstop": 48, "cd": null, "concerto": 1000, "hit_frames": [68, 73, 86, 98, 111, 124, 133], "zones": [{"kind": "ms", "from": 6, "to": 54}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Strike Before Ready", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 65, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [82], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Outro: Sheath Fallen, New Shoots Revealed", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 65, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [82], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Inkwash 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 19, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Inkwash 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 29, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 400, "hit_frames": [10, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Inkwash 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 64, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 946, "hit_frames": [24, 34, 40, 49, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Inkwash (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 90, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1120, "hit_frames": [30, 41, 53, 65], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Inkwash (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 46, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 672, "hit_frames": [40], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thus Spoke the Blade - Inkwash 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 39, "cancel": 21, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 480, "hit_frames": [10, 21], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thus Spoke the Blade - Inkwash 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 60, "cancel": 47, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 747, "hit_frames": [18, 27, 47], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thus Spoke the Blade - Inkwash 3", "tags": [], "genre": "BASIC", "hits": 6, "frames": 45, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 707, "hit_frames": [20, 56, 56, 74, 92, 110], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Thus Spoke the Blade - Inkwash 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 53, "cancel": 6, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 833, "hit_frames": [6], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Inkwash", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 51, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 667, "hit_frames": [20], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Thus Spoke the Blade - To Teach", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 99, "cancel": 86, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1475, "hit_frames": [38, 46, 56, 68, 86], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Thus Spoke the Blade - To Save", "tags": [], "genre": "HEAVY", "hits": 6, "frames": 57, "cancel": 57, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 678, "hit_frames": [40, 40, 46, 58, 64, 70], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Thus Spoke the Blade - To Sacrifice", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 47, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 3701, "hit_frames": [32], "zones": []}, + {"section": "HEAVY ATTACK", "name": "S6: Thus I Spoke", "tags": [], "genre": "SKILL", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Through the Groves", "tags": [], "genre": "SKILL", "hits": 3, "frames": 81, "cancel": 64, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 840, "concerto": 1000, "hit_frames": [34, 49, 64], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Undaunted Wayfarer (Hold)", "tags": [], "genre": "SKILL", "hits": 5, "frames": 44, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 840, "concerto": 1000, "hit_frames": [17, 25, 46, 67, 85], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Straw Cape in Drizzly Rain", "tags": [], "genre": "SKILL", "hits": 5, "frames": 44, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -6000, "hit_frames": [16, 25, 46, 67, 85], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Sundering Strike", "tags": ["TIME STOP 230F", "MOTION STOP 230F"], "genre": "LIBERATION", "hits": 1, "frames": 230, "cancel": 230, "noswap": 0, "tstop": 230, "mstop": 230, "cd": 1500, "concerto": 2000, "hit_frames": [210], "zones": [{"kind": "ms", "from": 0, "to": 230}, {"kind": "ts", "from": 0, "to": 230}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1308": { + "name": "Rebecca", + "wuwalab_name": "Rebecca", + "url": "https://wuwalab.com/characters/rebecca", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Yo, It's Big Boomin' Time!", "tags": ["MOTION STOP 4–93F"], "genre": "INTRO", "hits": 8, "frames": 96, "cancel": 72, "noswap": 96, "tstop": 0, "mstop": 89, "cd": null, "concerto": 1000, "hit_frames": [44, 46, 48, 50, 52, 54, 56, 72], "zones": [{"kind": "ms", "from": 4, "to": 93}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Hey, Leadhead, Come 'n' Get Me!", "tags": ["MOTION STOP 4–61F"], "genre": "INTRO", "hits": 6, "frames": 89, "cancel": 59, "noswap": 63, "tstop": 0, "mstop": 57, "cd": null, "concerto": 1000, "hit_frames": [29, 53, 54, 55, 57, 59], "zones": [{"kind": "ms", "from": 4, "to": 61}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Preem Choom", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Outro: Turret", "tags": [], "genre": "NONE", "hits": 70, "frames": 840, "cancel": 840, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276, 288, 300, 312, 324, 336, 348, 360, 372, 384, 396, 408, 420, 432, 444, 456, 468, 480, 492, 504, 516, 528, 540, 552, 564, 576, 588, 600, 612, 624, 636, 648, 660, 672, 684, 696, 708, 720, 732, 744, 756, 768, 780, 792, 804, 816, 828, 840], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Outro: Turret (Lucy)", "tags": [], "genre": "NONE", "hits": 20, "frames": 240, "cancel": 240, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Huntress 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 26, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 218, "hit_frames": [8, 22], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Huntress 2", "tags": [], "genre": "BASIC", "hits": 5, "frames": 40, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 285, "hit_frames": [12, 13, 14, 16, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Huntress 3", "tags": [], "genre": "BASIC", "hits": 1, "frames": 42, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 325, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Huntress (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 42, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 325, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Huntress (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 43, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 403, "hit_frames": [34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Guts 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 56, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 366, "hit_frames": [14, 31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Guts 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 33, "cancel": 11, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [11], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Guts 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 85, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 667, "hit_frames": [13, 23, 58], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Guts (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 60, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 467, "hit_frames": [34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Guts (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 57, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [34], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Huntress", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 32, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [26, 32], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Huntress (Loop)", "tags": [], "genre": "NONE", "hits": 1, "frames": 6, "cancel": 6, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 50, "hit_frames": [6], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Eat Lead!", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 36, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 360, "hit_frames": [0, 13], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 3: Guts", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 68, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [50], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: It's Big Boomin' Time!", "tags": [], "genre": "SKILL", "hits": 8, "frames": 87, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 60, "concerto": 700, "hit_frames": [30, 32, 34, 36, 38, 40, 42, 60], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Come 'n' Get Me!", "tags": [], "genre": "SKILL", "hits": 7, "frames": 110, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 60, "concerto": 700, "hit_frames": [12, 36, 62, 64, 66, 68, 70], "zones": []}, + {"section": "FORTE", "name": "Forte: Rat-tat-tat!", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 110, "cancel": 84, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2000, "hit_frames": [12, 46, 50, 56, 84], "zones": []}, + {"section": "FORTE", "name": "Forte: Bang-bang-bang!", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 90, "cancel": 64, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2000, "hit_frames": [64], "zones": []}, + {"section": "DASH", "name": "Tactical Dodge: Huntress", "tags": [], "genre": "DASH", "hits": 5, "frames": 36, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [6, 10, 16, 20, 30], "zones": []}, + {"section": "DASH", "name": "Tactical Dodge: Guts", "tags": [], "genre": "DASH", "hits": 1, "frames": 40, "cancel": 6, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [6], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Party 'til Dawn!", "tags": ["TIME STOP 180F", "MOTION STOP 180F"], "genre": "LIBERATION", "hits": 0, "frames": 180, "cancel": 180, "noswap": 0, "tstop": 180, "mstop": 180, "cd": 1500, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Mk. 31 HMG", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 56, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Mk. 31 HMG 1st Enh.", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 112, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Mk. 31 HMG 2nd Enh.", "tags": [], "genre": "NONE", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 167, "hit_frames": [0], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: BOOM! Fireworks!", "tags": [], "genre": "LIBERATION", "hits": 2, "frames": 133, "cancel": 133, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [66, 79], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 96F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 96, "cancel": 96, "noswap": 96, "tstop": 96, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 96}]}, + {"section": "TUNE BREAK SKILL", "name": "Hack Response - Meltdown", "tags": [], "genre": "TUNEBREAK", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [0], "zones": []} + ] + }, + "1501": { + "name": "Rover: Spectro", + "wuwalab_name": "Rover: Spectro", + "url": "https://wuwalab.com/characters/roverspectro", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Waveshock", "tags": ["MOTION STOP 4–51F"], "genre": "INTRO", "hits": 1, "frames": 72, "cancel": 56, "noswap": 71, "tstop": 0, "mstop": 47, "cd": null, "concerto": 1000, "hit_frames": [56], "zones": [{"kind": "ms", "from": 4, "to": 51}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Instant", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Vibration Manifestation 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 200, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Vibration Manifestation 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 25, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 400, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Vibration Manifestation 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 24, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 400, "hit_frames": [14, 20, 26, 32, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Vibration Manifestation 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 49, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Vibration Manifestation (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 27, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 360, "hit_frames": [17], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Vibration Manifestation (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 54, "cancel": 39, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [39], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Resonating Echoes", "tags": [], "genre": "BASIC", "hits": 2, "frames": 60, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [9, 36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 1: Vibration Manifestation", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 33, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 455, "hit_frames": [12, 18, 24, 30, 36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 2: Resonance", "tags": [], "genre": "BASIC", "hits": 1, "frames": 27, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 360, "hit_frames": [17], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy 3: Aftertune", "tags": [], "genre": "BASIC", "hits": 1, "frames": 41, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [12], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Resonating Slashes", "tags": [], "genre": "SKILL", "hits": 1, "frames": 41, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 1000, "hit_frames": [16], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Resonating Spin", "tags": [], "genre": "SKILL", "hits": 2, "frames": 58, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 2000, "hit_frames": [19, 31], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Echoing Orchestra", "tags": ["TIME STOP 129F", "MOTION STOP 79F"], "genre": "LIBERATION", "hits": 2, "frames": 129, "cancel": 129, "noswap": 0, "tstop": 129, "mstop": 79, "cd": 1200, "concerto": 2000, "hit_frames": [78, 123], "zones": [{"kind": "ms", "from": 0, "to": 79}, {"kind": "ts", "from": 0, "to": 129}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 91F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 91, "cancel": 91, "noswap": 91, "tstop": 91, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 91}]} + ] + }, + "1102": { + "name": "Sanhua", + "wuwalab_name": "Sanhua", + "url": "https://wuwalab.com/characters/sanhua", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Freezing Thorns", "tags": ["MOTION STOP 52F"], "genre": "INTRO", "hits": 1, "frames": 60, "cancel": 54, "noswap": 70, "tstop": 0, "mstop": 52, "cd": null, "concerto": 1000, "hit_frames": [54], "zones": [{"kind": "ms", "from": 0, "to": 52}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Silversnow", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 23, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 200, "hit_frames": [13], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 33, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 400, "hit_frames": [24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 33, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light 3", "tags": [], "genre": "BASIC", "hits": 4, "frames": 36, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [18, 24, 30, 36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light 4", "tags": [], "genre": "BASIC", "hits": 2, "frames": 36, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [12, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light 5", "tags": [], "genre": "BASIC", "hits": 1, "frames": 109, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1000, "hit_frames": [32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Frigid Light (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [34], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Cursor (Tap)", "tags": [], "genre": "NONE", "hits": 0, "frames": 30, "cancel": 30, "noswap": 30, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Cursor (Forte)", "tags": [], "genre": "NONE", "hits": 0, "frames": 50, "cancel": 50, "noswap": 50, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Cursor (Forte Buffer)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Frigid Light", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 50, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [10, 14, 18, 22, 36], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Detonate", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 101, "cancel": 101, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1500, "hit_frames": [51, 101], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Eternal Frost", "tags": [], "genre": "SKILL", "hits": 1, "frames": 60, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 1500, "hit_frames": [18], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Glacial Gaze", "tags": ["TIME STOP 90F", "MOTION STOP 90F"], "genre": "LIBERATION", "hits": 1, "frames": 97, "cancel": 97, "noswap": 0, "tstop": 90, "mstop": 90, "cd": 960, "concerto": 2000, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 90}, {"kind": "ts", "from": 0, "to": 90}]}, + {"section": "FORTE", "name": "Glacier", "tags": [], "genre": "NONE", "hits": 1, "frames": 75, "cancel": 75, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1500, "hit_frames": [75], "zones": []}, + {"section": "FORTE", "name": "Ice Prism", "tags": [], "genre": "NONE", "hits": 1, "frames": 62, "cancel": 62, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1500, "hit_frames": [62], "zones": []}, + {"section": "FORTE", "name": "Ice Thorn", "tags": [], "genre": "NONE", "hits": 1, "frames": 75, "cancel": 75, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [75], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 92F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 92, "cancel": 92, "noswap": 92, "tstop": 92, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 92}]} + ] + }, + "1505": { + "name": "Shorekeeper", + "wuwalab_name": "Shorekeeper", + "url": "https://wuwalab.com/characters/shorekeeper", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Enlightenment", "tags": ["MOTION STOP 6–34F"], "genre": "INTRO", "hits": 5, "frames": 85, "cancel": 44, "noswap": 65, "tstop": 0, "mstop": 28, "cd": null, "concerto": 2000, "hit_frames": [83, 85, 87, 90, 92], "zones": [{"kind": "ms", "from": 6, "to": 34}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Discernment", "tags": ["TIME STOP 6–140F", "MOTION STOP 6–140F"], "genre": "INTRO", "hits": 3, "frames": 215, "cancel": 143, "noswap": 0, "tstop": 134, "mstop": 134, "cd": null, "concerto": 2000, "hit_frames": [143, 155, 167], "zones": [{"kind": "ms", "from": 6, "to": 140}, {"kind": "ts", "from": 6, "to": 140}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Binary Butterfly", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Origin Calculus 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 23, "cancel": 15, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 160, "hit_frames": [15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Origin Calculus 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 240, "hit_frames": [8, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Origin Calculus 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 2, "frames": 33, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 880, "hit_frames": [8, 20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Origin Calculus 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 47, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 354, "hit_frames": [20, 29, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Origin Calculus 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 51, "cancel": 25, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 366, "hit_frames": [25], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Origin Calculus (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 50, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [46], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Chaos Theory", "tags": [], "genre": "SKILL", "hits": 5, "frames": 39, "cancel": 17, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 960, "concerto": 3000, "hit_frames": [55, 57, 60, 62, 65], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: End Loop", "tags": ["TIME STOP 207F", "MOTION STOP 207F"], "genre": "LIBERATION", "hits": 0, "frames": 207, "cancel": 207, "noswap": 0, "tstop": 207, "mstop": 207, "cd": 1500, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Stellarealm (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "FORTE", "name": "Forte: Illation", "tags": [], "genre": "HEAVY", "hits": 5, "frames": 48, "cancel": 5, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [22, 34, 46, 58, 64], "zones": []}, + {"section": "FORTE", "name": "Forte: Transmutation", "tags": [], "genre": "BASIC", "hits": 1, "frames": 50, "cancel": 46, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1100, "hit_frames": [46], "zones": []}, + {"section": "FORTE", "name": "Flare Star Butterfly", "tags": [], "genre": "NONE", "hits": 1, "frames": 36, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [36], "zones": []}, + {"section": "FORTE", "name": "Flare Star Butterflies", "tags": [], "genre": "NONE", "hits": 5, "frames": 56, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [42, 45, 48, 53, 56], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1412": { + "name": "Sigrika", + "wuwalab_name": "Sigrika", + "url": "https://wuwalab.com/characters/sigrika", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Solsworn Etymology", "tags": ["MOTION STOP 5–42F"], "genre": "INTRO", "hits": 1, "frames": 58, "cancel": 46, "noswap": 48, "tstop": 0, "mstop": 37, "cd": null, "concerto": 1000, "hit_frames": [46], "zones": [{"kind": "ms", "from": 5, "to": 42}]}, + {"section": "INTRO / OUTRO", "name": "Outro: In This Very Moment", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 48, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 22, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 167, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 41, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 318, "hit_frames": [18, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 44, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 350, "hit_frames": [8, 16, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 42, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 650, "hit_frames": [8, 16, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 78, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 651, "hit_frames": [22, 28, 46, 72], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 44, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: One, Two, Three (Mid-Air Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 44, "cancel": 38, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 610, "hit_frames": [38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Elucidated", "tags": [], "genre": "BASIC", "hits": 4, "frames": 66, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 519, "hit_frames": [8, 16, 24, 66], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Decipher (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 4, "frames": 66, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 519, "hit_frames": [8, 16, 24, 66], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: One, Two, Three", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 42, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 366, "hit_frames": [32, 32], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: BOOMY BOOM!", "tags": [], "genre": "SKILL", "hits": 4, "frames": 56, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 600, "concerto": 450, "hit_frames": [10, 16, 36, 48], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: BIG BOOMY BOOM!", "tags": [], "genre": "SKILL", "hits": 5, "frames": 56, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 486, "hit_frames": [6, 18, 30, 42, 54], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Soliskin to the Aid", "tags": [], "genre": "SKILL", "hits": 4, "frames": 54, "cancel": 50, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 431, "hit_frames": [6, 16, 24, 50], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Where Trust Leads Me!", "tags": ["TIME STOP 228F", "MOTION STOP 228F"], "genre": "LIBERATION", "hits": 1, "frames": 228, "cancel": 228, "noswap": 0, "tstop": 228, "mstop": 228, "cd": 1500, "concerto": 2000, "hit_frames": [176], "zones": [{"kind": "ms", "from": 0, "to": 228}, {"kind": "ts", "from": 0, "to": 228}]}, + {"section": "FORTE", "name": "Forte: Schemata of Runes", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 76, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 50, "hit_frames": [12], "zones": []}, + {"section": "FORTE", "name": "Runic Outburst", "tags": [], "genre": "NONE", "hits": 3, "frames": 68, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [20, 36, 68], "zones": []}, + {"section": "FORTE", "name": "Runic Chain Whip", "tags": [], "genre": "NONE", "hits": 7, "frames": 66, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 703, "hit_frames": [6, 12, 18, 24, 54, 60, 66], "zones": []}, + {"section": "FORTE", "name": "Runic Soliskin", "tags": [], "genre": "NONE", "hits": 6, "frames": 78, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 700, "hit_frames": [18, 36, 45, 54, 63, 78], "zones": []}, + {"section": "FORTE", "name": "Forte: Learn My True Name", "tags": [], "genre": "NONE", "hits": 2, "frames": 138, "cancel": 86, "noswap": 132, "tstop": 0, "mstop": 0, "cd": 1500, "concerto": 3000, "hit_frames": [86, 130], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 92F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 92, "cancel": 92, "noswap": 92, "tstop": 92, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 92}]} + ] + }, + "1110": { + "name": "Suisui", + "wuwalab_name": "SuiSui", + "url": "https://wuwalab.com/characters/suisui", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Tinkling Jade", "tags": ["MOTION STOP 4–58F"], "genre": "INTRO", "hits": 1, "frames": 78, "cancel": 60, "noswap": 60, "tstop": 0, "mstop": 54, "cd": null, "concerto": 1960, "hit_frames": [60], "zones": [{"kind": "ms", "from": 4, "to": 58}]}, + {"section": "INTRO / OUTRO", "name": "Intro: Tinkling Jade (Enhanced)", "tags": ["MOTION STOP 4–58F"], "genre": "INTRO", "hits": 1, "frames": 78, "cancel": 60, "noswap": 60, "tstop": 0, "mstop": 54, "cd": null, "concerto": 3760, "hit_frames": [60], "zones": [{"kind": "ms", "from": 4, "to": 58}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Rippling Waters", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Enrichment (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Enrichment - S4 (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Spring's Birth (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Spring's Birth - S4 (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "Plume Step (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Zephyr 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 24, "cancel": 2, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 318, "hit_frames": [8], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Zephyr 2", "tags": [], "genre": "BASIC", "hits": 2, "frames": 46, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 614, "hit_frames": [12, 30], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Zephyr 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 51, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 703, "hit_frames": [22, 29, 41], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Zephyr 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 35, "cancel": 26, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 860, "hit_frames": [13, 17, 26], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Zephyr 4", "tags": [], "genre": "BASIC", "hits": 6, "frames": 60, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [2, 8, 14, 20, 26, 41], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Zephyr (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 45, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 761, "hit_frames": [37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drizzle 1", "tags": [], "genre": "BASIC", "hits": 4, "frames": 31, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 396, "hit_frames": [20, 24, 31, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drizzle 2", "tags": [], "genre": "BASIC", "hits": 7, "frames": 70, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [23, 32, 36, 40, 43, 47, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drizzle 3", "tags": [], "genre": "BASIC", "hits": 12, "frames": 66, "cancel": 56, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 840, "hit_frames": [11, 16, 20, 25, 29, 34, 38, 43, 47, 52, 56, 61], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Drizzle 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 64, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [48], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Swallow's Cut", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 33, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 903, "hit_frames": [33], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Drizzle", "tags": [], "genre": "HEAVY", "hits": 11, "frames": 97, "cancel": 82, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1200, "hit_frames": [14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 82], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Basic: Illuminating Dew", "tags": [], "genre": "BASIC", "hits": 1, "frames": 64, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 880, "hit_frames": [30], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Zephyr", "tags": [], "genre": "SKILL", "hits": 6, "frames": 55, "cancel": 55, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 720, "hit_frames": [25, 31, 37, 43, 49, 55], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Awakening Spring", "tags": [], "genre": "SKILL", "hits": 1, "frames": 78, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 960, "hit_frames": [60], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Awakening Spring (Enhanced)", "tags": [], "genre": "SKILL", "hits": 1, "frames": 78, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 2760, "hit_frames": [60], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Drizzle", "tags": [], "genre": "SKILL", "hits": 7, "frames": 54, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 720, "hit_frames": [26, 30, 35, 40, 45, 50, 57], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Song of Thoroughfare", "tags": ["TIME STOP 264F", "MOTION STOP 264F"], "genre": "LIBERATION", "hits": 0, "frames": 264, "cancel": 264, "noswap": 0, "tstop": 264, "mstop": 264, "cd": 1500, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1601": { + "name": "Taoqi", + "wuwalab_name": "Taoqi", + "url": "https://wuwalab.com/characters/taoqi", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Defense Formation", "tags": ["MOTION STOP 4–61F"], "genre": "INTRO", "hits": 1, "frames": 104, "cancel": 64, "noswap": 90, "tstop": 0, "mstop": 57, "cd": null, "concerto": 1000, "hit_frames": [64], "zones": [{"kind": "ms", "from": 4, "to": 61}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Iron Will", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Concealed Edge 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 29, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 350, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Concealed Edge 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 35, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 390, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Concealed Edge 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 74, "cancel": 62, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1300, "hit_frames": [36, 62], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Concealed Edge 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 72, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 810, "hit_frames": [36], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Concealed Edge (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 48, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 693, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Concealed Edge (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 72, "cancel": 42, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [42], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Concealed Edge", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 77, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 346, "hit_frames": [30], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Strategic Parry", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 61, "cancel": 4, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [4], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Fortified Defense", "tags": [], "genre": "SKILL", "hits": 1, "frames": 55, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 900, "concerto": 1500, "hit_frames": [32], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Unmovable", "tags": ["TIME STOP 145F", "MOTION STOP 103F"], "genre": "LIBERATION", "hits": 1, "frames": 145, "cancel": 140, "noswap": 0, "tstop": 145, "mstop": 103, "cd": 960, "concerto": 2000, "hit_frames": [100], "zones": [{"kind": "ms", "from": 0, "to": 103}, {"kind": "ts", "from": 0, "to": 145}]}, + {"section": "FORTE", "name": "Forte: Timed Counters 1", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 27, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [16], "zones": []}, + {"section": "FORTE", "name": "Forte: Timed Counters 2", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 33, "cancel": 22, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [22], "zones": []}, + {"section": "FORTE", "name": "Forte: Timed Counters 3", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 79, "cancel": 56, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 500, "hit_frames": [56], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 64F"], "genre": "TUNEBREAK", "hits": 3, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 64, "cd": null, "concerto": 0, "hit_frames": [4, 26, 66], "zones": [{"kind": "ms", "from": 0, "to": 64}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1503": { + "name": "Verina", + "wuwalab_name": "Verina", + "url": "https://wuwalab.com/characters/verina", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Verdant Growth", "tags": ["MOTION STOP 4–54F"], "genre": "INTRO", "hits": 1, "frames": 98, "cancel": 76, "noswap": 83, "tstop": 0, "mstop": 50, "cd": null, "concerto": 1800, "hit_frames": [62], "zones": [{"kind": "ms", "from": 4, "to": 54}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Blossom", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "INTRO / OUTRO", "name": "S1: Blossom (Healing)", "tags": [], "genre": "NONE", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 29, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 304, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 35, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 411, "hit_frames": [20], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 43, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 410, "hit_frames": [20, 24], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 1, "frames": 20, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 560, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 4", "tags": [], "genre": "BASIC", "hits": 1, "frames": 43, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 541, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 5", "tags": [], "genre": "BASIC", "hits": 1, "frames": 58, "cancel": 10, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 576, "hit_frames": [10], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 15, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 453, "hit_frames": [15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Starflower Blooms 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 21, "cancel": 15, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 453, "hit_frames": [15], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 18, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 428, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Starflower Blooms 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 18, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 428, "hit_frames": [12], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Cultivation 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 33, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 612, "hit_frames": [24, 28, 32], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Starflower Blooms 3 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 3, "frames": 33, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 612, "hit_frames": [24, 28, 32], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Cultivation", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 48, "cancel": 9, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 800, "hit_frames": [9], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Cultivation (Mid-Air)", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 36, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 100, "hit_frames": [28], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Starflower Blooms", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 48, "cancel": 20, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 466, "hit_frames": [9, 20], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Botany Experiment", "tags": [], "genre": "SKILL", "hits": 4, "frames": 65, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 720, "concerto": 3000, "hit_frames": [24, 27, 30, 45], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Arboreal Flourish", "tags": ["TIME STOP 106F", "MOTION STOP 59F"], "genre": "LIBERATION", "hits": 1, "frames": 150, "cancel": 150, "noswap": 0, "tstop": 106, "mstop": 59, "cd": 1500, "concerto": 2000, "hit_frames": [58], "zones": [{"kind": "ms", "from": 0, "to": 59}, {"kind": "ts", "from": 0, "to": 106}]}, + {"section": "RESONANCE LIBERATION", "name": "Photosynthesis Mark", "tags": [], "genre": "NONE", "hits": 1, "frames": 22, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [22], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Photosynthesis Mark - S3", "tags": [], "genre": "NONE", "hits": 1, "frames": 22, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [22], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Joyous Harvest - S6", "tags": [], "genre": "NONE", "hits": 1, "frames": 32, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [32], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1610": { + "name": "Yangyang: Xuanling", + "wuwalab_name": "XuanLing", + "url": "https://wuwalab.com/characters/xuanling", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Skytouch", "tags": ["MOTION STOP 5–35F"], "genre": "INTRO", "hits": 1, "frames": 47, "cancel": 40, "noswap": 44, "tstop": 0, "mstop": 30, "cd": null, "concerto": 1000, "hit_frames": [40], "zones": [{"kind": "ms", "from": 5, "to": 35}]}, + {"section": "INTRO / OUTRO", "name": "Intro 2: Aria of the Feather", "tags": ["MOTION STOP 5–35F"], "genre": "INTRO", "hits": 1, "frames": 47, "cancel": 40, "noswap": 44, "tstop": 0, "mstop": 30, "cd": null, "concerto": 1000, "hit_frames": [40], "zones": [{"kind": "ms", "from": 5, "to": 35}]}, + {"section": "INTRO / OUTRO", "name": "Outro: As the Wind Wills", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Azure Sword 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 19, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 150, "hit_frames": [14], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Azure Sword 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 44, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 318, "hit_frames": [6, 15, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Azure Sword 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 44, "cancel": 37, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 618, "hit_frames": [6, 15, 37], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Azure Sword 3", "tags": [], "genre": "BASIC", "hits": 2, "frames": 43, "cancel": 35, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 317, "hit_frames": [14, 35], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Azure Sword 4", "tags": [], "genre": "BASIC", "hits": 3, "frames": 76, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 585, "hit_frames": [12, 21, 41], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Azure Sword (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 53, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Sword 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 33, "cancel": 27, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [16, 27], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Sword 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 42, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 318, "hit_frames": [12, 20, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Sword 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 42, "cancel": 12, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 618, "hit_frames": [12, 20, 29], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Sword 3", "tags": [], "genre": "BASIC", "hits": 5, "frames": 33, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 236, "hit_frames": [16, 16, 25, 34, 54], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Sword 4", "tags": [], "genre": "BASIC", "hits": 3, "frames": 93, "cancel": 54, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 750, "hit_frames": [50, 52, 54], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Sword (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 53, "cancel": 34, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 310, "hit_frames": [34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Feather Fall (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 4, "frames": 76, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 312, "hit_frames": [10, 19, 28, 58], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Azure Sword", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 105, "cancel": 70, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 867, "hit_frames": [52, 60, 70], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Feather Sword", "tags": [], "genre": "HEAVY", "hits": 2, "frames": 45, "cancel": 45, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 467, "hit_frames": [36, 45], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sword Stance Switch - Azure", "tags": [], "genre": "SKILL", "hits": 4, "frames": 59, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 367, "hit_frames": [22, 41, 50, 59], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sword Stance Switch - Azure (Directional)", "tags": [], "genre": "SKILL", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sword Stance Switch - Feather", "tags": [], "genre": "SKILL", "hits": 3, "frames": 44, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 318, "hit_frames": [28, 36, 44], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sword Stance Switch - Feather (Directional)", "tags": [], "genre": "SKILL", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sword Stance Flow - Azure", "tags": [], "genre": "SKILL", "hits": 4, "frames": 59, "cancel": 41, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1047, "hit_frames": [22, 41, 50, 59], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Sword Stance Flow - Feather", "tags": [], "genre": "SKILL", "hits": 3, "frames": 44, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1500, "hit_frames": [28, 36, 44], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Hush of a Thousand Voices", "tags": ["TIME STOP 300F", "MOTION STOP 300F"], "genre": "LIBERATION", "hits": 1, "frames": 300, "cancel": 300, "noswap": 0, "tstop": 300, "mstop": 300, "cd": 1500, "concerto": 2000, "hit_frames": [252], "zones": [{"kind": "ms", "from": 0, "to": 300}, {"kind": "ts", "from": 0, "to": 300}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Shadow of Xuanling", "tags": [], "genre": "NONE", "hits": 1, "frames": 12, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [12], "zones": []}, + {"section": "FORTE", "name": "Basic: Havoc in Bloom 1", "tags": [], "genre": "BASIC", "hits": 3, "frames": 38, "cancel": 14, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 336, "hit_frames": [14, 20, 26], "zones": []}, + {"section": "FORTE", "name": "Basic: Havoc in Bloom 2", "tags": [], "genre": "BASIC", "hits": 3, "frames": 69, "cancel": 60, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 626, "hit_frames": [36, 48, 60], "zones": []}, + {"section": "FORTE", "name": "Basic: Havoc in Bloom 3", "tags": [], "genre": "BASIC", "hits": 6, "frames": 108, "cancel": 72, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 867, "hit_frames": [6, 15, 24, 33, 42, 72], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 5, "frames": 90, "cancel": 90, "noswap": 89, "tstop": 90, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [30, 36, 42, 48, 72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 90}]} + ] + }, + "1507": { + "name": "Zani", + "wuwalab_name": "Zani", + "url": "https://wuwalab.com/characters/zani", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Immediate Execution", "tags": ["MOTION STOP 4–80F"], "genre": "INTRO", "hits": 6, "frames": 90, "cancel": 78, "noswap": 80, "tstop": 0, "mstop": 76, "cd": null, "concerto": 1000, "hit_frames": [20, 26, 32, 38, 44, 78], "zones": [{"kind": "ms", "from": 4, "to": 80}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Beacon for the Future", "tags": [], "genre": "OUTRO", "hits": 1, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [0], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Routine Negotiation 1", "tags": [], "genre": "BASIC", "hits": 1, "frames": 24, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 185, "hit_frames": [16], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Routine Negotiation 2", "tags": [], "genre": "BASIC", "hits": 1, "frames": 32, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 250, "hit_frames": [18], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Routine Negotiation 3", "tags": [], "genre": "BASIC", "hits": 3, "frames": 57, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 402, "hit_frames": [12, 33, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Routine Negotiation 3 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 3, "frames": 57, "cancel": 51, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 702, "hit_frames": [12, 33, 51], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Routine Negotiation 4", "tags": [], "genre": "BASIC", "hits": 4, "frames": 103, "cancel": 77, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 852, "hit_frames": [26, 42, 69, 77], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Routine Negotiation (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 48, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 330, "hit_frames": [48], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Breakthrough", "tags": [], "genre": "BASIC", "hits": 8, "frames": 110, "cancel": 110, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 586, "hit_frames": [26, 50, 60, 70, 80, 90, 100, 110], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Heavy Slash Daybreak", "tags": [], "genre": "BASIC", "hits": 3, "frames": 31, "cancel": 31, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 300, "hit_frames": [20, 29, 31], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Heavy Slash Dawning", "tags": [], "genre": "BASIC", "hits": 5, "frames": 79, "cancel": 71, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [18, 28, 56, 67, 71], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Heavy Slash Dawning (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 5, "frames": 77, "cancel": 69, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 600, "hit_frames": [12, 22, 50, 63, 69], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Heavy Slash Nightfall", "tags": [], "genre": "BASIC", "hits": 9, "frames": 151, "cancel": 133, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 1200, "hit_frames": [22, 32, 51, 61, 80, 90, 94, 118, 133], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Heavy Slash Nightfall (Half)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 76, "cancel": 58, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 792, "hit_frames": [6, 15, 19, 43, 58], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Routine Negotiation", "tags": [], "genre": "HEAVY", "hits": 4, "frames": 63, "cancel": 47, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 520, "hit_frames": [12, 35, 41, 47], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Standard Defense Protocol", "tags": [], "genre": "SKILL", "hits": 1, "frames": 18, "cancel": 8, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 300, "concerto": 500, "hit_frames": [8], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Scorching Light (Tap)", "tags": [], "genre": "SKILL", "hits": 0, "frames": 30, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill: Scorching Light (Hold)", "tags": [], "genre": "SKILL", "hits": 0, "frames": 78, "cancel": 78, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [], "zones": []}, + {"section": "FORTE", "name": "Forte: Targeted Action", "tags": [], "genre": "SKILL", "hits": 3, "frames": 134, "cancel": 128, "noswap": 38, "tstop": 0, "mstop": 0, "cd": null, "concerto": 2000, "hit_frames": [66, 117, 128], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Rekindle", "tags": ["TIME STOP 200F", "MOTION STOP 200F"], "genre": "LIBERATION", "hits": 1, "frames": 200, "cancel": 200, "noswap": 0, "tstop": 200, "mstop": 200, "cd": 1500, "concerto": 2000, "hit_frames": [120], "zones": [{"kind": "ms", "from": 0, "to": 200}, {"kind": "ts", "from": 0, "to": 200}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: The Last Stand", "tags": ["TIME STOP 134F", "MOTION STOP 134F"], "genre": "LIBERATION", "hits": 2, "frames": 134, "cancel": 134, "noswap": 0, "tstop": 134, "mstop": 134, "cd": null, "concerto": 1000, "hit_frames": [44, 128], "zones": [{"kind": "ms", "from": 0, "to": 134}, {"kind": "ts", "from": 0, "to": 134}]}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 94F", "MOTION STOP 70F"], "genre": "TUNEBREAK", "hits": 1, "frames": 94, "cancel": 94, "noswap": 94, "tstop": 94, "mstop": 70, "cd": null, "concerto": 0, "hit_frames": [72], "zones": [{"kind": "ms", "from": 0, "to": 70}, {"kind": "ts", "from": 0, "to": 94}]} + ] + }, + "1105": { + "name": "Zhezhi", + "wuwalab_name": "Zhezhi", + "url": "https://wuwalab.com/characters/zhezhi", + "abilities": [ + {"section": "INTRO / OUTRO", "name": "Intro: Radiant Ruin", "tags": ["MOTION STOP 5–59F"], "genre": "INTRO", "hits": 3, "frames": 80, "cancel": 36, "noswap": 80, "tstop": 0, "mstop": 54, "cd": null, "concerto": 1000, "hit_frames": [66, 72, 78], "zones": [{"kind": "ms", "from": 5, "to": 59}]}, + {"section": "INTRO / OUTRO", "name": "Outro: Carve and Draw", "tags": [], "genre": "OUTRO", "hits": 0, "frames": 0, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": -10000, "hit_frames": [], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Dimming Brush 1", "tags": [], "genre": "BASIC", "hits": 2, "frames": 36, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 480, "hit_frames": [18, 34], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Dimming Brush 2", "tags": [], "genre": "BASIC", "hits": 5, "frames": 44, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 595, "hit_frames": [30, 34, 38, 42, 46], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Dimming Brush 2 (Dodge Counter)", "tags": [], "genre": "COUNTER", "hits": 5, "frames": 34, "cancel": 13, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [18, 23, 26, 30, 35], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Dimming Brush 3", "tags": [], "genre": "BASIC", "hits": 1, "frames": 60, "cancel": 40, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 768, "hit_frames": [44], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Dimming Brush 1 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 5, "frames": 34, "cancel": 16, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 595, "hit_frames": [22, 26, 30, 34, 38], "zones": []}, + {"section": "BASIC ATTACK", "name": "Basic: Dimming Brush 2 (Mid-Air)", "tags": [], "genre": "BASIC", "hits": 1, "frames": 66, "cancel": 36, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 496, "hit_frames": [39], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Dimming Brush", "tags": [], "genre": "HEAVY", "hits": 1, "frames": 40, "cancel": 32, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 534, "hit_frames": [34], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Conjuration", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 75, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 669, "hit_frames": [28, 34, 40], "zones": []}, + {"section": "HEAVY ATTACK", "name": "Heavy: Conjuration (Mid-Air)", "tags": [], "genre": "HEAVY", "hits": 3, "frames": 38, "cancel": 30, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 669, "hit_frames": [30, 36, 42], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Manifestation", "tags": [], "genre": "SKILL", "hits": 3, "frames": 40, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 800, "hit_frames": [26, 32, 38], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 1: Manifestation (Hold)", "tags": [], "genre": "SKILL", "hits": 3, "frames": 42, "cancel": 24, "noswap": 0, "tstop": 0, "mstop": 0, "cd": 360, "concerto": 800, "hit_frames": [26, 32, 38], "zones": []}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Stroke of Genius", "tags": ["MOTION STOP 12F"], "genre": "SKILL", "hits": 1, "frames": 52, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 12, "cd": null, "concerto": 1300, "hit_frames": [46], "zones": [{"kind": "ms", "from": 0, "to": 12}]}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Stroke of Genius - S6", "tags": ["MOTION STOP 12F"], "genre": "SKILL", "hits": 2, "frames": 52, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 12, "cd": null, "concerto": 1300, "hit_frames": [46, 52], "zones": [{"kind": "ms", "from": 0, "to": 12}]}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Stroke of Genius (Mid-Air)", "tags": ["MOTION STOP 12F"], "genre": "SKILL", "hits": 1, "frames": 52, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 12, "cd": null, "concerto": 1300, "hit_frames": [46], "zones": [{"kind": "ms", "from": 0, "to": 12}]}, + {"section": "RESONANCE SKILL", "name": "Skill 2: Stroke of Genius (Mid-Air) - S6", "tags": ["MOTION STOP 12F"], "genre": "SKILL", "hits": 2, "frames": 52, "cancel": 18, "noswap": 0, "tstop": 0, "mstop": 12, "cd": null, "concerto": 1300, "hit_frames": [46, 52], "zones": [{"kind": "ms", "from": 0, "to": 12}]}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Creation's Zenith", "tags": ["MOTION STOP 52F"], "genre": "SKILL", "hits": 3, "frames": 72, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 52, "cd": null, "concerto": 1300, "hit_frames": [56, 68, 79], "zones": [{"kind": "ms", "from": 0, "to": 52}]}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Creation's Zenith - S6", "tags": ["MOTION STOP 52F"], "genre": "SKILL", "hits": 4, "frames": 72, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 52, "cd": null, "concerto": 1300, "hit_frames": [56, 62, 68, 79], "zones": [{"kind": "ms", "from": 0, "to": 52}]}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Creation's Zenith (Mid-Air)", "tags": ["MOTION STOP 52F"], "genre": "SKILL", "hits": 3, "frames": 72, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 52, "cd": null, "concerto": 1300, "hit_frames": [56, 68, 79], "zones": [{"kind": "ms", "from": 0, "to": 52}]}, + {"section": "RESONANCE SKILL", "name": "Skill 3: Creation's Zenith (Mid-Air) - S6", "tags": ["MOTION STOP 52F"], "genre": "SKILL", "hits": 4, "frames": 72, "cancel": 28, "noswap": 0, "tstop": 0, "mstop": 52, "cd": null, "concerto": 1300, "hit_frames": [56, 62, 68, 79], "zones": [{"kind": "ms", "from": 0, "to": 52}]}, + {"section": "RESONANCE LIBERATION", "name": "Liberation: Living Canvas", "tags": ["TIME STOP 166F", "MOTION STOP 166F"], "genre": "LIBERATION", "hits": 0, "frames": 166, "cancel": 166, "noswap": 0, "tstop": 166, "mstop": 166, "cd": 1500, "concerto": 2000, "hit_frames": [], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Inklit Spirit", "tags": [], "genre": "NONE", "hits": 1, "frames": 37, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [37], "zones": []}, + {"section": "RESONANCE LIBERATION", "name": "Inklit Spirit - S5", "tags": [], "genre": "NONE", "hits": 2, "frames": 37, "cancel": 0, "noswap": 0, "tstop": 0, "mstop": 0, "cd": null, "concerto": 0, "hit_frames": [37, 37], "zones": []}, + {"section": "TUNE BREAK SKILL", "name": "Tune Break Skill", "tags": ["TIME STOP 90F", "MOTION STOP 54F"], "genre": "TUNEBREAK", "hits": 1, "frames": 90, "cancel": 90, "noswap": 90, "tstop": 90, "mstop": 54, "cd": null, "concerto": 0, "hit_frames": [56], "zones": [{"kind": "ms", "from": 0, "to": 54}, {"kind": "ts", "from": 0, "to": 90}]} + ] + } + } +} diff --git a/static/index.html b/static/index.html index 31b0505..46deea2 100644 --- a/static/index.html +++ b/static/index.html @@ -15,6 +15,9 @@
    + + +