# JoyDigi — GameHiHi có dữ liệu và công cụ chỉnh sửa

## 0. Danh mục 368 ảnh được phân loại (bản cập nhật)

- Mở `joydigi-assets/DANH_MUC_ANH_474.html` để xem hình từng asset, tìm tên, lọc nhóm, mở file PNG.
- 368 PNG nằm trong `joydigi-assets/sprites/01_NHAN_VAT/` đến `09_KY_THUAT_HITBOX/`, chia tiếp theo nhóm con. **Tên file không đổi; bytes ảnh không đổi.**
- Bộ ảnh `nhoc-hihi-assets.zip` đã được ghép vào game bằng `joydigi-tools/joydigi-nhoc-hihi-pack.py`: nhân vật HiHi (nón lá, áo xanh chữ HiHi, tay trắng, quần xanh, giày trắng) trên rig 17 bộ phận; nền xa `pozadie*` cắt dải từ 4 tranh làng quê, mờ dần phía trên để hòa với bầu trời (lớp xa vẫn được game nhuộm tint); cụm nhà `pozadie_mesto1` và `dom.*` → nhà ngói, nhà tranh, cổng làng; `tree_top_01/02` → bụi cây, bụi tre; `branch_` → dây leo; `metal_fence_panel` → rào tre; `street_light` → cột đèn lồng; `bedna`, `zemina_krabica.animation-*` → thùng gỗ; `balvan` → cuộn rơm; `odpadkovy` → chum đất; `chest` → rương gỗ; `bomba.*` (trừ landmine) → pháo; `cirkularka` → bánh xe gai; `okno.*` để trong suốt. Chữ menu đổi thành "developed by gamehihi". Giữ nguyên: gạch đất/bê tông, cỏ, cây dừa, bẫy gai gỗ, UI, hiệu ứng.
- Nhân vật Nhóc HiHi (bộ NhocHiHi_Fix_CoTay_v4) đã thay toàn bộ 18 PNG bộ phận, rig `joydigi-character.scon` được retarget theo tỷ lệ v4, collider/manifest cập nhật; xem `joydigi-tools/nhoc-hihi-rig/GHI_CHU_RIG.md` (script `rebuild_character.py`, bộ nguồn v4 tại `joydigi-assets/source/nhoc-hihi-v4/`).
- Nền đất đổi sang xanh lá ở mọi màn: tint của `poda1` (0.24, 0.50, 0.18) và khối `zemina_kocka` nâu (0.35, 0.62, 0.26) trong layout; màu theo vùng (biome 2/3/4) nằm ở expression 704, 706, 707, 712, 713 trong `joydigi-expressions.js` (dùng bởi `environment-rules` event 929–938) đã đổi sang các tông xanh tương ứng.
- Game chỉ còn **một nhân vật duy nhất** (skin mặc định, `glo.typek = 101`). Cửa hàng skin trong menu, nút Skins trong 64 màn, 106 ảnh biến thể của các skin khác và logic chọn/mở khoá skin đã được gỡ. Xem mục *Thay nhân vật*.
- `joydigi-assets.json` đã được đồng bộ toàn bộ 368 đường dẫn, giữ nguyên binding/type/frame/pivot/scale/logic.
- Trang `index.html` hiện hỗ trợ bộ lọc chín nhóm ảnh, cộng âm thanh và mục đã thay.
- Danh mục CSV/JSON và các bảng xem nhanh ở `joydigi-assets/DANH_MUC_SPRITES/`.
- Có thể nhập bản thử đã xuất trước đây: loader và editor tự chuyển đường dẫn sprite dạng phẳng cũ sang vị trí mới nếu tìm thấy ID trùng tên file.
- Để xem hoặc chơi game, **chạy HTTP local**, không mở trực tiếp index.html bằng `file://`.

**Trạng thái bàn giao: bản tái cấu trúc một phần, chưa đạt toàn bộ yêu cầu viết lại gameplay và kiểm thử trình duyệt.**

Đã triển khai mã mới cho quản lý asset, tách atlas, biên dịch event có tên, biên dịch màn chơi, xem animation SCON, công cụ chỉnh sửa và áp dụng thay đổi. Gameplay vẫn thực thi bằng Construct/Spriter/Box2D đi kèm bản xuất. Không có `.c3p`, không khẳng định khôi phục được mã nguồn gốc. Việc giữ lớp thực thi này tránh thay 64 màn bằng một game mẫu có hành vi khác.

Đọc thêm [phân tích nguồn](joydigi-ANALYSIS.md) và [kết quả kiểm thử](joydigi-TEST-REPORT.md). Hai ảnh trước/sau trong `joydigi-tests/joydigi-evidence/` là ảnh render SCON độc lập, **không phải ảnh chụp game đã chạy trong trình duyệt**.

## 1. Chạy bằng HTTP local

Cần Python 3.10 trở lên và trình duyệt có WebGL/WebAssembly. Chạy trong thư mục chứa README:

```bash
python3 joydigi-tools/joydigi-serve.py --port 8000
```

Trên Windows có thể dùng `py -3` thay `python3`. Mở `http://127.0.0.1:8000/`.

Không mở `index.html` bằng `file://`. Không cần npm, tài khoản, Poki hay Internet để chạy. Các thư viện runtime đã nằm trong ZIP. Chính sách CSP của hai trang chỉ cho tải tài nguyên cùng origin, `blob:` và `data:`. Không đăng ký service worker.

Bốn tab:

- **Asset:** tìm ID, xem ảnh/âm thanh, thay PNG/WebP, chỉnh scale/pivot/offset, nhập/xuất bộ thay đổi.
- **Animation:** xem 15 animation của nhân vật ghép xương, chỉnh tốc độ, dừng và kéo thời gian.
- **Màn chơi:** chọn một trong 64 màn, tìm vật thể theo type/UID/spawn/goal, chỉnh X/Y/kích thước/góc và xuất JSON.
- **Chơi thử:** khởi tạo game bằng dữ liệu được biên dịch từ các nguồn chỉnh sửa, hoặc tải lại sau khi đổi asset.

Lần tải đầu phải đọc các layout và ảnh riêng lẻ nên có thể chậm hơn build dùng atlas. Game nhúng cần được bấm vào để nhận bàn phím. Các điều khiển vẫn do rule Keyboard/Touch gốc xử lý.

## 2. Thay ảnh bằng công cụ

1. Tìm ID và chọn thẻ asset.
2. Chọn PNG/WebP ở ô **Ảnh thay thế**; hoặc chép tệp vào `joydigi-assets/custom/` rồi nhập đường dẫn tương đối với thư mục dự án.
3. Chỉnh Scale X/Y, Pivot X/Y hoặc dịch X/Y nếu cần.
4. Bấm **Áp dụng vào bản thử**. Thay đổi đang nằm trong bộ nhớ localStorage của đúng origin trình duyệt, chưa sửa tệp trên đĩa.
5. Với nhân vật, mở tab Animation để xem các khớp. Với game, bấm **Tải lại từ manifest**.
6. Bấm **Xuất thay đổi** để tải `joydigi-changes.json`. Ảnh chọn từ máy được nhúng trong JSON, không phải đường dẫn tạm bị mất khi đóng tab.
7. Áp dụng vĩnh viễn vào dự án:

```bash
python3 joydigi-tools/joydigi-apply-changes.py /duong-dan/joydigi-changes.json
```

Script ghi ảnh vào `joydigi-assets/custom/`, cập nhật manifest/JSON màn chơi và sao lưu các tệp bị ghi đè vào `joydigi-backups/`. Để khôi phục, chép các tệp trong thư mục backup tương ứng về đúng đường dẫn của chúng trong dự án; tệp custom mới có thể giữ lại vì không còn được tham chiếu.

Sau khi áp dụng trên đĩa, bấm **Bỏ bản thử**, rồi tải lại game để tránh override cũ che thay đổi mới. Bỏ bản thử chỉ xóa override của editor; không xóa save game hay các tệp đã áp dụng. Xuất JSON trước nếu cần giữ bản thử.

Ảnh trên 2 MB nên chép vào dự án rồi nhập đường dẫn. Bộ nhớ localStorage có hạn; nếu đầy, xuất bộ thay đổi ngay. Không nhập đường dẫn web ngoài dự án.

### Thay nhân vật

Game chỉ có một nhân vật. Mỗi bộ phận còn đúng một animation, giữ nguyên tên biến thể mà skin mặc định (101) vẫn dùng, nên ID ảnh không đổi:

| Bộ phận | ID ảnh còn lại |
|---|---|
| Thân, cánh tay, cẳng tay | `panko1_player_p_torso_idle.typek1.000`, `panko1_player_rameno1/2.typek1.000`, `panko1_player_laket1/2.typek1.000` |
| Bụng, đùi, bắp chân | `panko1_player_brucho.typek1.000`, `panko1_player_stehno2.typek1.000`, `panko1_player_p_thigh_idle_0.typek1.000`, `panko1_player_lytko1/2.typek1.000` |
| Bàn chân | `panko1_player_chodidlo1/2.typek3.000` |
| Bàn tay (2 frame) | `panko1_player_dlan1/2.typek0.000` và `.001` |
| Đầu (2 frame) | `panko1_player_hlava.typek0.000` và `.001` |
| Mũ, súng | `panko1_player_black_cap.typek0.000`, `panko1_player_pistol01.typek0.000` |

Tìm `panko1_player_` trong tab Asset để xem đủ 20 ảnh. Ánh xạ bộ phận → biến thể nằm trong hàm `nastav_typka` của `joydigi-character-rules.json` (chỉ còn nhánh skin 101); Loại sprite `typek_obr` (ảnh thu nhỏ skin, không còn dùng) đã được đổi tên thành `pozadie_tranh`: hai animation `dong_que` và `ho_sen` chứa tranh làng quê 1672×941 (`07_PHONG_NEN/01_Canh_Nen/pozadie_tranh.*.png`) dùng làm nền phủ cả bầu trời; màn 2 dùng `dong_que`, màn 5 dùng `ho_sen` (instance trên layer `pozadie2`, parallax 0).

- Đổi riêng từng bộ phận; không thay toàn nhân vật bằng một tấm hình vào slot thân.
- Giữ hướng vẽ, khoảng trống và vị trí khớp tương ứng. Xem `idle`, `walk`, `jump_loop`, `stand_up` và các animation đứng dậy.
- Đổi tất cả frame cần dùng: bàn tay và đầu có 2 frame. Thay frame 0 không tự thay frame 1.
- Ảnh có độ phân giải khác được đưa về **khung logic cũ**. Collider, image point và khớp không tự đổi theo số pixel mới.
- Pivot trong công cụ là **pivot hình ảnh** để dịch ảnh bên trong khung cũ; pivot vật lý và điểm gắn xương vẫn lấy từ dữ liệu riêng.
- Scale lớn/offset quá xa có thể cắt hình tại biên khung. Nếu cần nhân vật lớn hơn về mặt vật lý, phải sửa collider, kích thước instance, SCON và joint một cách đồng bộ; công cụ này không tự làm việc đó.

SCON nằm ở `joydigi-source/joydigi-character.scon`, được giữ nguyên cấu trúc `entity`, `animation`, `mainline`, `timeline`, `bone_ref`, `object_ref`, parent, spin, pivot và z-index. Có thể chỉnh bằng công cụ SCON phù hợp rồi thay đúng tệp. Không đổi tên timeline tùy ý: chúng liên kết với loại sprite `panko1_Player_<tên timeline>`.

### Thay vật thể/mặt đất

Ví dụ `zemina_kocka.animation-1.000`, `bedna.bedna2.000` và `bedna.bedna2.001`. Chọn ảnh mới hoặc đổi `path`. Body size, mật độ, ma sát, hệ số nảy và polygon không lấy từ ảnh mới.

Lưu ý mặt đất gốc có ảnh trắng được nhuộm màu bằng tint/effect của layout. Ảnh màu thay vào có thể vẫn bị tint của instance tác động; sửa `tint` trong JSON màn nếu muốn thay cả cách tô màu.

### Thay nền

Ví dụ `pozadie_a.default.000`; tìm `pozadie`, `oblak`, `trava`, `tree` để xem các lớp trang trí. Vị trí, góc, kích thước và parallax nằm trong JSON màn. Thay ảnh không tự chỉnh tốc độ parallax.

### Thay nút giao diện

Ví dụ `home_btn.default.000`, `restart_btn.default.000`, các ID chứa `btn` hoặc `toggle`. Giữ khung logic cũ để vùng bấm không đổi. Một object có thể có nhiều animation như `skip`, `how`; sửa đúng trạng thái cần dùng. Chữ spritefont dùng texture riêng, không phải font hệ thống.

## 3. Manifest và collider

`joydigi-assets.json` là nguồn tra cứu asset bằng ID. Đường dẫn không nằm rải rác trong gameplay mới. Một frame gồm:

```json
{
  "kind": "image",
  "path": "joydigi-assets/sprites/01_NHAN_VAT/01_Nhan_Vat_Chinh_Tach_Lop/13_Than_Nguoi_Idle/panko1_player_p_torso_idle.typek1.000.png",
  "displaySize": [193, 176],
  "pivot": [0, 0],
  "scale": [1, 1],
  "offset": [0, 0]
}
```

Các trường bổ sung giữ duration, image point, atlas rectangle, cờ xoay, trim, binding type/animation/frame và SHA-256 pixel. `objects` quản lý speed/loop/repeat/ping-pong và danh sách frame. `rig` trỏ tới SCON và các animation. `sourceAtlases`/`auxiliary` là danh mục atlas/icon nguồn để đối chiếu, không phải các slot gameplay đã tách.

Collider nằm ở `joydigi-source/joydigi-colliders.json`. Khi thay ảnh, loader cố ý lấy kích thước logic, pivot va chạm, image point và polygon từ tệp này. **`displaySize` trong manifest mô tả kích thước logic; sửa riêng trường này không làm đổi collider hoặc body.** Muốn đổi kích thước vật lý phải chủ động sửa các nguồn vật lý tương ứng.

Atlas được cắt theo tọa độ thật. 172 frame được giải xoay 90° đúng theo routine `ExtractImageToCanvas` của runtime. Không có chia lưới tùy ý, không tự crop lại theo alpha. Export không cung cấp trim ngoài khung frame; trường trim ghi rõ giới hạn này, không tự bịa canvas chưa-trim của tác giả.

## 4. Chỉnh màn chơi

Các tệp nằm trong `joydigi-levels/`; `joydigi-layout001.json` đến `joydigi-layout064.json` là màn chơi, ngoài ra có loader/menu/repository.

Mỗi layer giữ `name`, `parallax`, `opacity`; mỗi object có `uid`, `type`, `x`, `y`, `width`, `height`, `angle` (radian), `origin`, `tint`, `variables`, `role` và cấu hình `physics` nếu có.

- `panko_init` được đánh dấu `role: "spawn"`; có thể có nhiều marker/checkpoint. Không tự gộp chúng thành một điểm.
- `ciel` được đánh dấu `role: "goal"`.
- `physics` có các trường `immovable`, `collisionMask`, `preventRotation`, `density`, `friction`, `restitution`, `linearDamping`, `angularDamping`, `bullet`, `enabled`.
- `compatibility` giữ metadata runtime chưa chuyển thành trường có tên: liên kết UID/template, effect, trạng thái addon, v.v. Compiler lấy giá trị từ các trường có tên để ghi đè đúng slot. Không xóa payload này.

Có thể sửa trực tiếp JSON rồi tải lại game; không cần rebuild bằng npm. File JSON xuất từ editor cần chép thay tệp tương ứng trong `joydigi-levels/`. Hoặc dùng bộ `joydigi-changes.json` để áp dụng ảnh và màn cùng lúc.

Nút mở game vẫn đi qua loader/menu để giữ khởi tạo global và save; chọn màn tương ứng trong menu game. Không nhảy trực tiếp vào layout và bỏ qua các bước khởi tạo.

Editor hiện hỗ trợ **sửa instance đã có**. Chưa có công cụ an toàn để tạo object mới kèm joint/template/UID hoặc xóa dây chuyền đối tượng liên kết. Bản đồ là sơ đồ vị trí, không phải WYSIWYG đầy đủ của renderer Construct.

## 5. Cấu trúc mã nguồn

| Thành phần | Nguồn có thể chỉnh sửa | Phần còn dùng engine |
|---|---|---|
| Tải asset | `joydigi-assets.js`, manifest, collider | Upload texture/WebGL |
| Nhân vật | `joydigi-character-rules.json`, `joydigi-character.js` | Chọn instance, trigger và vòng lặp event |
| Điều khiển | `joydigi-controls-rules.json`, `joydigi-controls.js` | Keyboard/Touch adapter |
| Vật lý | `joydigi-physics-rules.json`, cấu hình `physics` trong màn | Box2D WASM, joint và adapter Construct |
| Animation | `joydigi-animation.js`, SCON, `joydigi-animation-rules.json` | Spriter adapter trong game; preview dùng sampler mới |
| Camera | `joydigi-camera-rules.json`, `joydigi-camera.js` | Camera và viewport của runtime |
| Màn chơi | `joydigi-levels.js`, `joydigi-levels/*.json` | Tạo instance từ ABI |
| Giao diện | `joydigi-ui-rules.json`, `joydigi-studio.js`, `joydigi-studio.css` | UI trong game vẫn dùng event gốc |
| Tiến trình | `joydigi-progress-rules.json`, `joydigi-progress.js` | Save game LocalStorage của Construct |
| Logic chung | `joydigi-events.js`, các sheet JSON, `joydigi-expressions.js` | Thực thi condition/action và expression binding |

Các adapter `.js` cho character/physics/camera **không phải engine gameplay viết lại hoàn toàn**. Logic có tên nằm trong các rule JSON. Đây là một dạng source khai báo có thể biên dịch về ABI runtime, không phải việc đổi tên vài file build để gọi là source gốc.

Một action đã được giải mã có `target: "koleso"`, `operation: "Behaviors.Physics.Acts.SetIterations"`, và hai parameter `literal: 24`, `literal: 9`. Sửa literal thực sự thay tham số khi chạy; compiler tạo factory riêng nếu cần để không vô tình đổi mọi nơi dùng chung hằng số.

Biểu thức phức tạp còn ở `joydigi-expressions.js` và binding `payload` của parameter. Chúng vẫn dùng giao diện nội bộ runtime và tên biến cục bộ được xuất; chưa chuyển hết thành hàm gameplay có tên tiếng Anh/Việt. Đừng sửa chỉ số binding nếu chưa hiểu schema. Đây là phần cần tiếp tục tái tạo để có source độc lập, dễ bảo trì toàn bộ.

## 6. Kiểm thử có thể chạy lại

Không cần npm để chạy kiểm tra dữ liệu/Box2D và ghi patch:

```bash
node joydigi-tests/joydigi-data-test.cjs
python3 joydigi-tests/joydigi-patch-test.py
```

Golden test so với baseline sẽ báo khác nếu bạn chủ động sửa gameplay/layout; đó không tự động là lỗi. Giữ bản backup baseline để đối chiếu.

Kiểm tra render trước/sau cần thư viện phát triển `@napi-rs/canvas` (không phải phụ thuộc của game):

```bash
npm install
npm run test:assets
```

Test dùng hai ảnh sẵn có làm ảnh thay để kiểm chứng đường nạp ảnh mới; không tạo artwork mới. Bộ thay mẫu ở `joydigi-tests/joydigi-example-changes.json` chưa được áp dụng mặc định vào dự án.

## 7. Phần chưa hoàn thành

- Chưa viết lại toàn bộ gameplay thành engine JavaScript độc lập khỏi Construct/Spriter.
- Chưa kiểm tra giao diện editor bằng trình duyệt và chưa xác nhận game khởi động/hoạt động end-to-end sau bootstrap mới.
- Chưa chơi thử di chuyển, nhảy, va chạm trong màn thật, chết/chơi lại, hoàn thành màn và lưu tiến trình; cũng chưa kiểm tra các bước đó sau thay asset.
- Chưa regression-test đủ 64 màn, ragdoll, thang/dây/đòn bẩy/băng chuyền/nước/bom và các tổ hợp joint.
- `melodia3.webm` được khai báo nhưng không có trong ZIP đầu vào. Manifest đánh dấu `available: false`; chưa thay bằng âm thanh khác.
- Không có dự án Construct `.c3p` và chưa có xác nhận trong editor Construct.

Không nên coi bản này là bản phát hành đã được nghiệm thu gameplay. Dữ liệu có bằng chứng bảo toàn tốt, nhưng không thay thế kiểm thử tích hợp trong trình duyệt.
