Alliance
Màn Alliance hiện các đồng minh (ally) mà người chơi sở hữu, cho người chơi chọn đồng minh mang vào trận, cho đồng minh lên cấp bằng vật phẩm EXP, nâng cấp Weapon của nó và đặt lại cấp. Cấp, chi phí, chỉ số và phần hoàn trả do server quyết định.
| Bạn dùng | Nó cho bạn |
|---|---|
IProjectVAllianceScreen | Screen contract: theo dõi các đồng minh, chọn, lên cấp, nâng cấp, đặt lại |
ProjectVAllianceView | Những gì cần vẽ: trạng thái đang tải, các đồng minh, đồng minh đang chọn, vật phẩm EXP |
ProjectVAllyView | Một đồng minh: cấp, EXP, chỉ số, kỹ năng Weapon, các bản xem trước |
ProjectVXpItemView | Một vật phẩm EXP và số lượng người chơi đang có |
ProjectVCharacterXpItemSelection | Các vật phẩm EXP và số lượng sẽ dùng |
ProjectVResult | Kết quả của một command (lệnh) |
Đồng minh dùng cùng chỉ số, vật phẩm EXP và kỹ năng Weapon như màn Character.
Bắt đầu nhanh
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SilverTiger.ProjectV.Unity.Presentation;
using SilverTiger.ProjectV.Unity.Presentation.Alliance;
using SilverTiger.ProjectV.Unity.Presentation.Character;
using UnityEngine;
using VContainer;
public sealed class AlliancePage : MonoBehaviour
{
private IProjectVAllianceScreen alliance = null!;
private IDisposable? observation;
[Inject]
public void Construct(IProjectVAllianceScreen screen) => alliance = screen;
private void OnEnable() => observation = alliance.Observe(Render);
private void OnDisable()
{
observation?.Dispose();
observation = null;
}
private void Render(ProjectVAllianceView view)
{
if (view.IsLoading)
{
ShowLoading(view.LoadError); // LoadError is null while the first load is running
return;
}
ShowAllies(view.Allies, view.SelectedAllyId);
ShowXpItems(view.XpItems);
}
public void OnSelectClicked(string allyId) => _ = RunAsync(alliance.SelectAllyAsync(allyId));
// The "+1" button; use XpForMaximumLevel() for "Max".
public void OnLevelUpClicked(ProjectVAllyView ally) => _ = RunAsync(alliance.LevelUpAsync(ally.Id, ally.XpForNextLevel()));
public void OnWeaponUpgradeClicked(string allyId) => _ = RunAsync(alliance.UpgradeWeaponSkillAsync(allyId));
public void OnResetClicked(string allyId) => _ = RunAsync(alliance.ResetLevelAsync(allyId));
private async Task RunAsync(Task<ProjectVResult> command)
{
var result = await command;
if (this != null && !result.IsSuccess)
{
ShowMessage(result.Message);
}
}
private void ShowLoading(string? error) { }
private void ShowAllies(IReadOnlyList<ProjectVAllyView> allies, string? selectedAllyId) { }
private void ShowXpItems(IReadOnlyList<ProjectVXpItemView> items) { }
private void ShowMessage(string message) { }
}
Đó là toàn bộ trang: theo dõi, vẽ, gọi command. Sau khi thành công bạn không
cần làm gì; Render được gọi lại với dữ liệu mới.
Cần vẽ gì
Render nhận một ProjectVAllianceView:
| Member | Kiểu | Dùng để |
|---|---|---|
IsLoading | bool | True cho tới khi các đồng minh về lần đầu. Hiện spinner |
LoadError | string? | Có giá trị khi lần tải đầu đó thất bại. Hiện nó cùng nút thử lại (Retry) đóng rồi mở lại trang |
Allies | IReadOnlyList<ProjectVAllyView> | Các đồng minh người chơi sở hữu |
SelectedAllyId | string? | Đồng minh người chơi mang vào trận; null khi chưa chọn ai. Đánh dấu (highlight) theo giá trị này, không theo cờ cục bộ |
XpItems | IReadOnlyList<ProjectVXpItemView> | Vật phẩm EXP: ItemId, Experience (EXP mỗi vật phẩm), Owned |
PendingUpgradeAllyId | string? | Đồng minh có lượt lên cấp chưa được xác nhận |
Find(allyId) | ProjectVAllyView? | Tra một đồng minh, ví dụ để làm mới panel nâng cấp đang mở |
Với mỗi ProjectVAllyView:
| Để hiện | Đọc |
|---|---|
| Cấp và EXP | Level, Experience; Upgrade cho những g ì cấp tiếp theo cần |
| Chỉ số | Stats (máu, tấn công, giáp, v.v.), như ở Character |
| Cấp tối đa | IsMaxLevel |
| Lựa chọn "+1" và "Max" | XpForNextLevel(), XpForMaximumLevel(); rỗng khi không thể đạt thêm cấp nào |
| Một lựa chọn sẽ làm gì | PreviewXp(items): CanSubmit, Message, GrantedExperience, TargetLevel |
| Nâng cấp Weapon | WeaponSkill: Level, MaxLevel, Costs, CanUpgrade, BlockedMessage |
| Phần hoàn trả khi đặt lại | LevelReset: CanReset, Message, Refunds |
| "Đang hoàn tất nâng cấp" | HasPendingUpgrade: lượt lên cấp của đồng minh này chưa được xác nhận |
| Tắt các nút lên cấp | IsBlockedByPendingUpgrade: lượt lên cấp của một đồng minh khác chưa được xác nhận |
View luôn được dựng từ dữ liệu đã xác nhận gần nhất, nên bản xem trước không
đổi trong lúc một command đang chạy. Ghép đồng minh với art và text của bạn
bằng Id trong một bảng của riêng bạn.
Chọn đồng minh
Task<ProjectVResult> SelectAllyAsync(string allyId)
| Tham số | Kiểu | Giá trị |
|---|---|---|
allyId | string | ally.Id của một đồng minh trong view hiện tại |
Trả về một ProjectVResult:
IsSuccess | Message | Chuyện gì đã xảy ra |
|---|---|---|
true | rỗng | Server đã xác nhận lựa chọn, hoặc đồng minh đã được chọn từ trước. Highlight chuyển khi Render được gọi |
false | lý do, sẵn để hiện | Lựa chọn không xảy ra, hoặc không xác nhận được (xem bên dưới) |
Khi thất bại — hiện Message nguyên văn:
| Tình huống | Người chơi thấy gì |
|---|---|
| Server từ chối lựa chọn | Thông báo của server |
allyId null hoặc rỗng | "The request was not confirmed. Please try again." |
| Mất kết nối hoặc hết thời gian chờ | Một thông báo mạng. Đừng tự động chọn lại |
SelectAllyAsync không ném exception khi allyId sai; nó trả về kết quả thất bại.
Mỗi lần một lựa chọn. Trong khi một lựa chọn đang chạy, mọi lời gọi
SelectAllyAsync khác đều nhận kết quả của lời gọi đầu tiên, kể cả cho một
đồng minh khác, và đồng minh khác đó không được chọn. Chờ kết quả đầu tiên
rồi mới chọn tiếp.
Lên cấp
Task<ProjectVResult> LevelUpAsync(string allyId, IReadOnlyList<ProjectVCharacterXpItemSelection> items)
| Tham số | Kiểu | Giá trị |
|---|---|---|
allyId | string | ally.Id của một đồng minh trong view hiện tại |
items | IReadOnlyList<ProjectVCharacterXpItemSelection> | Các vật phẩm EXP sẽ dùng, thường là ally.XpForNextLevel() hoặc ally.XpForMaximumLevel(). Bị bỏ qua khi đồng minh có lượt nâng cấp đang chờ |
Các vật phẩm được dùng trong một request và đồng minh nhận mọi cấp mà chúng đủ để đạt. Phần EXP chưa đủ cho cấp tiếp theo được giữ lại.
Ném exception ngay lập tức, trước khi gửi bất cứ thứ gì:
| Exception | Khi nào |
|---|---|
ArgumentException | allyId null hoặc rỗng, hoặc items có phần tử null hay không hợp lệ |
ArgumentNullException | items là null |
Trả về một ProjectVResult:
IsSuccess | Message | Chuyện gì đã xảy ra |
|---|---|---|
true | rỗng | Server đã xác nhận lượt lên cấp. Trang tự vẽ lại |
false | lý do, sẵn để hiện | Lượt lên cấp không xảy ra, hoặc không xác nhận được (xem bên dưới) |
Khi thất bại — hiện Message nguyên văn. Các lý do thường gặp:
| Tình huống | Người chơi thấy gì |
|---|---|
| Một đồng minh khác hoặc nhân vật có lượt lên cấp chưa được xác nhận | "Another ally or character has an upgrade pending." |
| Không chọn vật phẩm nào và không đủ EXP cho cấp tiếp theo | "Select enough EXP for the next level." |
| Không đủ tiền tệ cho cấp tiếp theo | "Not enough currency for the next level." |
| Đồng minh đã ở cấp tối đa | "Maximum level reached." |
| Các vật phẩm không thuộc sở hữu hoặc không phải vật phẩm EXP của đồng minh | "The selected EXP items are unavailable for this character kind." |
| Server từ chối lượt lên cấp | Thông báo của server |
| Mất kết nối hoặc hết thời gian chờ | Một thông báo mạng. Lượt lên cấp có thể vẫn đã thành công (xem bên dưới) |
Khi không rõ kết quả — ví dụ kết nối bị rớt — lượt lên cấp ở trạng thái
chờ. Đồng minh đó hiện HasPendingUpgrade và các đồng minh khác hiện
IsBlockedByPendingUpgrade. Lần LevelUpAsync tiếp theo cho đồng minh đó
sẽ hoàn tất lượt lên cấp đang chờ với chính các vật phẩm cũ trước khi làm gì
mới. Lượt lên cấp đang chờ được dùng chung với màn Character.
Bấm lần thứ hai vào cùng đồng minh trong lúc lượt lên cấp đang chạy sẽ nhận cùng kết quả thay vì tiêu hai lần.
Nâng cấp Weapon
Task<ProjectVResult> UpgradeWeaponSkillAsync(string allyId)
| Tham số | Kiểu | Giá trị |
|---|---|---|
allyId | string | ally.Id của một đồng minh trong view hiện tại |
Hiện chi phí và bật nút theo ally.WeaponSkill: Costs cho các dòng giá và
CanUpgrade cho nút.
Ném ArgumentException ngay lập tức khi allyId null hoặc rỗng.
Trả về một ProjectVResult:
IsSuccess | Message | Chuyện gì đã xảy ra |
|---|---|---|
true | rỗng | Server đã xác nhận nâng cấp. Trang tự vẽ lại |
false | lý do, sẵn để hiện | Nâng cấp không xảy ra, hoặc không xác nhận được (xem bên dưới) |
Khi thất bại — hiện Message nguyên văn. Các lý do thường gặp:
| Tình huống | Người chơi thấy gì |
|---|---|
| Weapon hiện không thể nâng cấp | WeaponSkill.BlockedMessage, ví dụ "Maximum level reached." |
| Các đồng minh chưa được tải | "Skill data unavailable." |
| Server từ chối nâng cấp | Thông báo của server |
| Mất kết nối hoặc hết thời gian chờ | Một thông báo mạng. Đừng tự động nâng cấp lại |
Bấm lần thứ hai trong lúc nâng cấp đang chạy sẽ nhận cùng kết quả.
Đặt lại cấp
Task<ProjectVResult> ResetLevelAsync(string allyId)
| Tham số | Kiểu | Giá trị |
|---|---|---|
allyId | string | ally.Id của một đồng minh trong view hiện tại |
Đồng minh quay về cấp 1 với 0 EXP, và các vật phẩm EXP nó đã dùng được trả
lại. Bậc Weapon và kỹ năng được giữ nguyên. Hiện phần hoàn trả từ
ally.LevelReset.Refunds và bật nút theo ally.LevelReset.CanReset.
Ném ArgumentException ngay lập tức khi allyId null hoặc rỗng.
Trả về một ProjectVResult:
IsSuccess | Message | Chuyện gì đã xảy ra |
|---|---|---|
true | rỗng | Server đã xác nhận đặt lại. Trang tự vẽ lại |
false | lý do, sẵn để hiện | Đặt lại không xảy ra, hoặc không xác nhận được (xem bên dưới) |
Khi thất bại — hiện Message nguyên văn. Các lý do thường gặp:
| Tình huống | Người chơi thấy gì |
|---|---|
| Hiện không được phép đặt lại | LevelReset.Message, lý do của server |
| Đồng minh không thuộc sở hữu | "This character is not owned." |
| Mất kết nối hoặc hết thời gian chờ | Một thông báo mạng. Đừng tự động đặt lại lần nữa |
Bấm lần thứ hai trong lúc đặt lại đang chạy sẽ nhận cùng kết quả.
Quy tắc
- Các command chạy lần lượt. Bấm một command khác sẽ chờ cho tới khi command đang chạy kết thúc.
- Màn tự đồng bộ lại. Sau một kết quả không xác nhận được, hoặc khi dữ liệu đang hiện đã cũ, màn sẽ tải lại các đồng minh ở nền. Lên cấp, nâng cấp Weapon và đặt lại sẽ chờ lần tải lại đó. Bạn không cần nút Refresh.
- Phần hoàn trả đến từ server. Đặt lại trả về đúng các vật phẩm EXP mà đồng minh đã dùng; EXP nhận bằng cách khác không được hoàn lại gì.
- Server kiểm tra mọi command.
CanUpgrade,CanResetvà các bản xem trước của view chỉ dùng để định hình các nút.
Lỗi thường gặp
- Chọn đồng minh thứ hai trước khi lựa chọn đầu tiên xong. Lời gọi thứ hai trả về kết quả của lời gọi đầu và không chọn đồng minh thứ hai.
- Tự tính chi phí, chỉ số hoặc phần hoàn trả trong Client. Dùng view; chỉ dùng asset cục bộ cho hình ảnh và animation.
- Tự trừ vật phẩm hoặc tiền tệ sau một command. Trang tự cập nhật; số của bạn sẽ sai.
- Highlight đồng minh đang chọn theo cờ cục bộ. Dùng
SelectedAllyId. - Truyền ID rỗng hoặc null cho các command lên cấp, Weapon hoặc đặt lại.
Chúng ném exception; hãy truyền
ally.Idtừ view hiện tại. - Dùng kết quả sau khi trang đã đóng. Kiểm tra
this != nullsauawait. - Không dispose (giải phóng) observation. Giữ
IDisposabletrả về từObservevà dispose nó trongOnDisablehoặcOnDestroy.
Presenter
IProjectVAlliancePresenter
là interface cấp thấp hơn nằm sau screen contract. Chỉ dùng nó nếu bạn cần
thứ mà màn không cung cấp:
| Member | Dùng để |
|---|---|
SubscribeAsync(listener) | Mọi ProjectVAllianceSnapshot: Status, Recovery, RequiresUpgradeSync, và một Error có Code và RequestId |
PendingProgression | Lượt lên cấp chưa được xác nhận, dùng chung với Character |
GetUpgradeState(allyId) | Đồng minh có thể lên cấp ngay không, và nếu không thì vì sao |
GetXpUpgradePreview(allyId, items) | Cấp, EXP và chỉ số mà các vật phẩm này sẽ cho |
GetLevelResetPreview(allyId) | Có được phép đặt lại không và vật phẩm EXP nào được trả lại |
SynchronizeAsync(token) | Tải lại từ server, cho một nút Sync rõ ràng |
RetryPendingProgressionAsync(token) | Hoàn tất lượt lên cấp chưa được xác nhận |
Các command của presenter nhận một ProjectVCharacterId và một
CancellationToken rồi trả về một
ProjectVAllianceCommandResult.
UpgradeAllyWithXpItemsAsync và UpgradeWeaponSkillAsync của nó ném
ArgumentException khi đồng minh không thuộc sở hữu.
Listener của SubscribeAsync có thể chạy ngoài main thread của Unity; hãy
chuyển về main thread trước khi đụng tới object Unity. Observe của màn luôn
vẽ (render) trên main thread.