Zeus — tài liệu ghép client
Slot 6×5 pay-anywhere / tumble (Gates of Olympus style). Client nói HTTP JSON với server game.
Trang này chỉ được serve khi ENVIRONMENT != production.
1. Tổng quan
| Mục | Giá trị |
|---|---|
| Grid | 6 cột × 5 hàng · reels[col][row], row 0 = trên cùng |
| Pay | Pay-anywhere: ≥ 8 symbol giống nhau ở bất kỳ vị trí |
| Tumble | Ô thắng biến mất, symbol còn lại rơi xuống, ô trống fill từ trên |
| Orbs | Symbol multiplier (id 10), value 2x–500x. Cộng dồn rồi nhân tổng win của spin |
| Free spins | Base: ≥ 4 scatter → 15 FS. Trong FS: ≥ 3 scatter → +5. Orbs được bank suốt feature |
| Buy FS | Giá = buy_free_spins_price × bet (mặc định 100× bet), guaranteed ≥ 4 scatter |
| Max win | 5000× bet. Chạm cap thì kết thúc round (kể cả FS còn lại) |
| Base path | /api/v1 |
GET /api/v1/gameplay trước khi spin.
Không persist FS qua restart process — reconnect thì đọc lại state từ /gameplay.
2. Luồng ghép
- Mở game → gửi
GET /api/v1/gameplayvới JWT. Render board preview, bet, paytable, money, FS state nếu đang giữa feature. - User chọn bet (chỉ khi
is_free_spin = false). - Spin thường →
POST /api/v1/spinsbody{"bet": ...}. - Play tuần tự từng phần tử
win_result[](tumble). Xem mục 8. - Sau tumble, apply multiplier nếu
applied_multiplier > 1. - Nếu
is_bonus_free_spin→ popup +N FS, chuyển UI sang free-spin mode. - Khi
is_free_spintrên response spin: không trừ bet, tiếp tụcPOST /spinscho tới khiis_finish_free_spin. - Buy bonus →
POST /api/v1/buy-freespins(không được gọi khi đang FS). - Luôn lấy số dư mới từ
user_moneycủa response vừa nhận.
3. Auth & envelope
Header
Authorization: Bearer <access_token> Content-Type: application/json
Mọi API dưới /api/v1 đều cần JWT. Trang docs không cần token.
Envelope
HTTP 200 + code = 0 là thành công. Payload nằm trong msg.
{
"code": 0,
"msg": { ... }
}
Lỗi nghiệp vụ thường trả HTTP 500, JWT sai/thiếu trả HTTP 401. Client luôn đọc field code.
| code | Ý nghĩa |
|---|---|
0 | OK |
-1 | Unknown / thiếu JWT / token invalid (tùy HTTP status) |
-2 | Invalid input (body JSON sai) |
69 | Wrong game state — chưa /gameplay, đang FS mà đổi bet, bet không hợp lệ |
1000 | Popup error (message i18n, dùng khi buy FS không hợp lệ) |
4. Grid & symbol
Mọi board (init_reels, win_result[].reels, cheat reels) đều là mảng cột:
reels[col][row] // col 0..5 trái → phải; row 0..4 trên → dưới orb_values[col][row] // cùng index; >0 chỉ khi symbol == 10 (multiplier)
Hàng đầu = row 0. Ô vàng = scatter (9). Ô xanh = multiplier orb (10).
| id | Symbol | Pay? |
|---|---|---|
| 0 | Crown | Có |
| 1 | Hourglass | Có |
| 2 | Ring | Có |
| 3 | Chalice | Có |
| 4 | Red gem | Có |
| 5 | Purple gem | Có |
| 6 | Yellow gem | Có |
| 7 | Green gem | Có |
| 8 | Blue gem | Có |
| 9 | Scatter (Zeus) | Không pay — trigger FS |
| 10 | Multiplier orb | Không pay — mang hệ số |
| -1 | Empty (nội bộ server) | Client không nhận ô này trên board đã fill |
Paytable
pay_table[symbol][band] là hệ số × bet. Band theo số lượng symbol:
| Count | Band index |
|---|---|
| 8–9 | 0 |
| 10–11 | 1 |
| 12–30 | 2 |
| < 8 | Không thắng |
Default: Crown [10, 25, 50] … Blue gem [0.25, 0.75, 2]. Luôn dùng pay_table từ /gameplay, đừng hardcode.
Công thức 1 line: win_amount = round(bet × multiplier). Tổng base win = cộng mọi line trên mọi tumble. Orbs nhân sau khi tumble xong.
5. GET /api/v1/gameplay
Gọi 1 lần khi vào game. Set session initiated — thiếu bước này thì POST /spins trả code 69.
GET /api/v1/gameplay
| Field | Type | Client dùng để |
|---|---|---|
init_reels | int[6][5] | Board preview idle (no-win). Không phải state đang chơi |
init_orb_values | int[6][5] | Orb đi kèm preview |
user_money | int64 | Chip hiện tại |
bet | int64 | Bet đang chọn trên server |
bets | int64[] | Các mức bet hợp lệ (gửi đúng 1 giá trị này) |
show_bets | int64[] | Giá trị hiển thị UI (cùng index với bets) |
pay_table | float[9][3] | Bảng trả thưởng 9 paying symbols |
is_free_spin | bool | Đang trong FS → restore UI FS, disable đổi bet / buy |
free_spins | int | Số FS còn lại |
max_free_spins | int | Mốc max để vẽ thanh FS |
free_spin_pot | int64 | Tổng win đang cộng dồn trong FS (chưa credit) |
total_multiplier | int | Bank orb trong FS (0 nếu base) |
buy_free_spins_price | int64 | Hệ số giá buy. Giá thật = field này × bet |
max_win_multiplier | int | Cap, mặc định 5000 |
{
"code": 0,
"msg": {
"init_reels": [[8,7,6,5,4],[3,2,1,0,8],[7,6,5,4,3],[2,1,0,8,7],[6,5,4,3,2],[1,0,8,7,6]],
"init_orb_values": [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],
"user_money": 500000,
"bet": 1000,
"bets": [200, 1000, 2000, 10000],
"show_bets": [200, 1000, 2000, 10000],
"pay_table": [[10,25,50],[2.5,10,25],[2,5,15],[1.5,4,12],[1,1.5,10],[0.8,1.2,8],[0.5,1,5],[0.4,0.9,4],[0.25,0.75,2]],
"is_free_spin": false,
"free_spins": 0,
"max_free_spins": 0,
"free_spin_pot": 0,
"total_multiplier": 0,
"buy_free_spins_price": 100,
"max_win_multiplier": 5000
}
}
6. POST /api/v1/spins
POST /api/v1/spins
{ "bet": 1000 }
- Phải gọi
/gameplaytrước. - Base: trừ
betchip ngay. FS: không trừ,betvẫn gửi (server bỏ qua đổi bet). - Không đổi bet khi đang FS.
reels/orb_valueschỉ dùng cheat (mục 11). Production bỏ qua.
SpinResult
| Field | Type | Ý nghĩa |
|---|---|---|
bet | int64 | Bet server đang dùng |
price | int64 | 0 với spin thường; giá đã trừ nếu response tới từ buy |
is_buy | bool | Spin này là kết quả buy FS |
user_money | int64 | Chip sau giao dịch của response này |
win_amount | int64 | Base: win đã nhân orb. FS giữa chừng: win spin này (chưa cộng vào tiền). FS xong: cả pot |
base_win_amount | int64 | Tổng line win trước khi nhân orb |
collected_multiplier | int | Tổng orb trên board cuối (0 nếu không thắng) |
applied_multiplier | int | Hệ số thực sự nhân vào win lần này (0/1 = không nhân) |
total_multiplier | int | Bank orb sau spin (FS). Base thường = collected hoặc 0 |
win_tier | int | 0 none · 1 >10× · 2 >20× · 3 >30× · 4 >50× (theo win_amount / bet) |
hit_max_win | bool | Chạm 5000× — dừng FS, credit cap |
is_free_spin | bool | true nếu spin vừa rồi nằm trong FS (không phải spin trigger) |
free_spins | int | Source of truth: số FS còn lại sau response |
free_spin_pot | int64 | Pot sau spin. = 0 khi FS kết thúc |
is_finish_free_spin | bool | Spin FS cuối (hoặc max-win). Credit pot vào user_money |
is_bonus_free_spin | bool | Spin này vừa award thêm FS (base trigger hoặc retrigger) |
number_of_bonus_free_spins | int | +15 (base) hoặc +5 (retrigger). Dùng cho popup |
max_free_spins | int | Max để vẽ progress |
scatter_count | int | Số scatter trên board cuối |
win_result | WinResult[] | Chuỗi tumble, play tuần tự |
WinResult (một bước tumble)
| Field | Ý nghĩa |
|---|---|
reels | Board trước khi xóa ô thắng của bước này |
orb_values | Hệ số orb cùng index với reels |
orbs | [{col,row,value}] — list orb đang trên board, tiện highlight |
lines | Các cụm thắng. Rỗng = tumble dừng |
positions | positions[col] = [row, ...] mọi ô thắng bước này |
win_amount | Tổng line win của bước này (chưa nhân orb) |
total_win_amount | Cộng dồn base win từ đầu spin tới bước này |
WinLine
| Field | Ý nghĩa |
|---|---|
symbol | 0–8 |
line_size | Số ô cùng symbol |
multiplier | Hệ số paytable (vd 10 = 10× bet) |
win_amount | Chip của line |
positions | positions[col] = [row,...] — ô thuộc line |
{
"code": 0,
"msg": {
"bet": 1000,
"price": 0,
"is_buy": false,
"user_money": 509000,
"win_amount": 10000,
"base_win_amount": 10000,
"collected_multiplier": 0,
"applied_multiplier": 0,
"total_multiplier": 0,
"win_tier": 0,
"hit_max_win": false,
"is_free_spin": false,
"free_spins": 15,
"free_spin_pot": 0,
"is_finish_free_spin": false,
"is_bonus_free_spin": true,
"number_of_bonus_free_spins": 15,
"max_free_spins": 15,
"scatter_count": 4,
"win_result": [
{
"lines": [{ "symbol": 0, "positions": [[0,1],[0],[0],[],[],[]], "win_amount": 10000, "line_size": 8, "multiplier": 10 }],
"reels": [[0,0,1,7,6],[0,0,2,1,7],[0,0,3,2,1],[0,1,4,3,2],[4,6,5,4,3],[5,7,6,5,4]],
"orb_values": [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],
"orbs": [],
"win_amount": 10000,
"total_win_amount": 10000,
"positions": [[0,1],[0],[0],[0],[],[]]
}
]
}
}
Sau bước có lines, phần tử tiếp theo trong mảng là board đã tumble. Phần tử cuối thường lines: [].
7. POST /api/v1/buy-freespins
POST /api/v1/buy-freespins
{ "bet": 1000 }
- Giá =
buy_free_spins_price × bet. Trừ ngay với reason buy. - Cấm khi đang FS → popup code 1000.
- Response cùng shape
SpinResult:is_buy = true,price= số chip đã trừ, guaranteed scatter trigger FS. - Play animation giống một spin (có
win_result), rồi vào FS mode.
!is_free_spin và user_money >= buy_free_spins_price * bet.8. Tumble / animation
- Với mỗi
win_result[i]: vẽreels+orb_values. - Nếu
lines.length > 0: highlightpositions/ từng line, cộngwin_amountvào counter base win. - Xóa các ô thắng. Symbol còn lại rơi xuống (gravity theo cột, row lớn = dưới). Ô trống fill từ trên (row 0).
- Board mới =
win_result[i+1].reels. Đừng tự random — tin server. - Phần tử cuối thường
lines = []: hết tumble. Đây là board để tính orb. - Nếu
applied_multiplier > 1: animation nhânbase_win_amount × applied_multiplier → win_amount. - Cập nhật money / FS UI từ field top-level, không suy ra từ tumble.
9. Multiplier orbs
Symbol 10. Value hợp lệ: 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 50, 100, 250, 500. Hiển thị số trên orb, lấy từ orb_values[col][row] hoặc orbs[].value.
| Mode | Khi nào apply | Công thức |
|---|---|---|
| Base | Spin có base_win_amount > 0 | applied = sum(orbs trên board cuối). Chỉ nhân spin này, không bank |
| Free spin | Spin thắng | Bank total_multiplier += collected. Rồi applied = total_multiplier (cả bank cũ + mới) |
| FS không thắng | — | Không cộng bank, không nhân. Orb trên board không thắng bị bỏ |
applied_multiplier ≤ 1 nghĩa là không nhân (giữ nguyên base win). Client chỉ play anim nhân khi > 1.
final_win = base_win // applied ≤ 1 final_win = base_win * applied_multiplier // applied > 1
10. Free spins
| Trigger | Điều kiện | Award |
|---|---|---|
| Base / buy | ≥ 4 scatter trên board cuối | +15 FS |
| Trong FS | ≥ 3 scatter | +5 FS (retrigger) |
- Spin trigger (base/buy) không set
is_free_spin. Win spin đó credit ngay. Sau đó UI vào FS vớifree_spins. - Các spin FS:
is_free_spin = true, không trừ bet. Win cộng vàofree_spin_pot, chưa cộng chip cho tới spin cuối. - Spin cuối:
is_finish_free_spin = true,win_amount= toàn bộ pot,user_moneyđã cộng pot,free_spin_pot = 0,total_multiplier = 0. hit_max_win: kết thúc FS ngay, credit cap, bỏ lượt còn lại.- Giữa FS: disable đổi bet và nút buy. Vẫn
POST /spinsvới bet hiện tại.
free_spins trên response. Popup +N dùng number_of_bonus_free_spins.11. Cheat (chỉ non-prod / cheater)
Trên môi trường không phải production, POST /spins nhận board cố định:
{
"bet": 1000,
"reels": [
[0, 0, 1, 7, 6],
[0, 0, 2, 1, 7],
[0, 0, 3, 2, 1],
[0, 1, 4, 3, 2],
[4, 6, 5, 4, 3],
[9, 9, 9, 9, 10]
],
"orb_values": [
[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,25]
]
}
- Đủ 6 cột, mỗi cột 5 hàng. Symbol 10 phải có
orb_values > 0(không gửi thì server gán 2). - Production: field này bị bỏ qua, luôn random.
12. Error & tiền
| Tình huống | Hành vi client |
|---|---|
| HTTP 401 | Token hết hạn / thiếu — relogin |
| code 69 | Gọi lại GET /gameplay, sync state, đừng spam spin |
| code 1000 | Hiện popup msg (string i18n) |
| code -1 khi spin/buy | Có thể hết tiền hoặc lỗi nội bộ — refresh money qua /gameplay |
| Spin khi user đang room PvP khác | HTTP 400 body "blocked" |
Khi nào chip đổi
| Event | Chip |
|---|---|
| Base spin | −bet ngay; +win_amount nếu thắng (cùng response) |
| Buy FS | −price ngay; +win của spin buy nếu có |
| FS giữa chừng | Không đổi. Chỉ tăng free_spin_pot |
| FS kết thúc / max win | +pot vào user_money |
13. Checklist client
- Vào game:
GET /gameplaytrước mọi spin. - Board luôn
[col][row], 6×5, row 0 = top. - Play tuần tự
win_result; tumble theo gravity; đừng tự gen symbol. - Highlight theo
lines/positions; orb theoorbs. - Nhân orb chỉ khi
applied_multiplier > 1. - Bet chỉ gửi giá trị trong
bets. - Giá buy =
buy_free_spins_price * bet. - FS: khóa bet/buy; tin
free_spins; credit chip chỉ khiis_finish_free_spin. - Win tier / max win: play fanfare theo
win_tiervàhit_max_win. - Restore: nếu
/gameplaytrảis_free_spin, nhảy thẳng vào FS UI. - Không gọi song song 2 spin cho 1 user — đợi response + animation xong.