Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0f71d1a
Add a Timings tab with WuwaLAB ability frame data
claude Oct 1, 2026
d1933d3
Fix the CI build job after requirements-build.txt was merged away
claude Oct 1, 2026
5f8675a
Left-align the Characters page layout
claude Oct 1, 2026
4b6f7a4
Drag to resize the sidebar and the Characters list
claude Oct 1, 2026
0ea5b32
Stop the browser from mixing old pages with new scripts
claude Oct 1, 2026
c95a013
fixed UI
WolfExplode Oct 1, 2026
e45aa06
Name Augusta's E, E, E as Strike, Leap, Plunge on the timeline
claude Oct 1, 2026
dbeae28
Show moves by their own name on the timeline
claude Oct 1, 2026
5aafb7b
Trim long move names, say just "Echo", name Iuno's Heavy "Flux"
claude Oct 1, 2026
f0a67ec
Timings tab: click a row for its frame strip; drop misread cooldowns
claude Oct 1, 2026
a4d8a8e
Track Concerto along a combo; Iuno's full-bar Heavy is Absolute Fullness
claude Oct 1, 2026
29835a9
Optional waits: -wait:0.15s can be cut short by the next key
claude Oct 1, 2026
627427d
Mark a tile's text in the Inputs box while it's dragged or clicked
claude Oct 1, 2026
cf246a9
A bare slot key is a plain swap; only wait(2, t) is an Intro
claude Oct 1, 2026
fcc8a46
Delete steps from a right-click menu; steps are always editable
claude Oct 1, 2026
09bac2d
Right-click a step to pick which move it is
claude Oct 1, 2026
bd3f1f3
Timings: Input column (lmb1, rmb, lmb, e...); drop the Moves list
claude Oct 1, 2026
894c6bf
Editable character notes that autosave; Ctrl+C/Ctrl+V tiles at the mouse
claude Oct 1, 2026
077ac86
Timings Input column: airborne -> lmb, arrows between keys pressed in…
claude Oct 1, 2026
30f82ce
Move pick menu shows each move's keys; mid-air basics keep their stage
claude Oct 1, 2026
e23805d
Move pick menu: rule out moves the step before makes impossible; show…
claude Oct 1, 2026
2453cd4
Name moves with WuwaLAB's names by default; one move is always checked
claude Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions Game_Wuthering_Waves.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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),
}

# -------------------------
Expand Down Expand Up @@ -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()
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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/`.

---

Expand Down
Loading
Loading