Compare commits

..

3 commits

Author SHA1 Message Date
99ab5b239d Implement 'avatar add_all' command 2024-08-07 13:35:34 +03:00
84397a847e Implement proper Avatar and Buddy UnitID (Base/Robot), refactor template id
TemplateID::new now returns Result<T, TemplateNotFoundError>
Cleanup code in some of new handlers
2024-08-06 21:42:03 +03:00
28864ff1e4 Teleport Map & Switching Dynamic Wallpaper Support, Gacha optimization (#2)
## Abstract

This PR implements

- Showing Teleports on map
- Quick Menu usage
- Dynamic Wallpaper switching
- Settings UP for Bangboo pool (via CLI)

## Support list

- The support of Teleport with map.
- Unlocking the 3 buttons (Interknow, DMs, Map) as a prerequisite of the previous feature. By pressing 'F' you can also modify the buttons in Quick Menu.
- Changing Dynamic Wallpaper to a specific one instead of being randomly chosen. **Notice that you can't use one's Dynamic Wallpaper if you don't own this agent.**
- Fixed some Gacha bugs, including:
  - Obtained items won't be sent to your bag unless you're pulling the standard banner;
  - Obtained items won't show in bag until relogin;
- Alternative command `gacha up` to choose the UP Bangboo. Start with `gacha up [player_uid]` and follow the guide to fill params.

## Principle

- Player's `UnlockModelBin` stores Quick Menu data, with the player's chosen buttons and their positions.
- Separate Gacha's DTO & Saving model to make it more standardized, and ensure ID validation is always performed.
- Player's bag information is now sync to client appropriately after finishing any gacha operations.
- `gacha up` search for pools with a `chooseable` Category Guarantee Policy, then list included items if user has provided the target pool's `gacha_schedule_id`.

## Known issues

- **Specifically for 1.1 Beta**, teleport may meet these issues leading to a 'black screen', forcing you to restart the client (or use `player kick` command as an alternative).
  - Don't try to teleport to `治安局光映分署`, it won't succeed. This is the 32nd teleport out of the 31 ones defined visible in assets.
  - Don't close the page after opening map. Teleport to somewhere.
- `gacha up` command is an alternative to in-game operation. You still can not see any bangboos in the collection when choosing Bangboo.

Co-authored-by: YYHEggEgg <53960525+YYHEggEgg@users.noreply.github.com>
Reviewed-on: NewEriduPubSec/JaneDoe-ZS#2
Co-authored-by: YYHEggEgg <yyheggegg@xeondev.com>
Co-committed-by: YYHEggEgg <yyheggegg@xeondev.com>
2024-08-06 17:15:04 +00:00
53 changed files with 11210 additions and 171 deletions

View file

@ -0,0 +1,266 @@
[
{
"ID": 50001,
"BuddyType": 1,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_50001",
"DIIDBBGLDOL": "Bangboo_Name_50001",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 9831098947874880184
},
{
"ID": 53001,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53001",
"DIIDBBGLDOL": "Bangboo_Name_53001",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 8017298617212498953
},
{
"ID": 53002,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53002",
"DIIDBBGLDOL": "Bangboo_Name_53002",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 15759244567804428825
},
{
"ID": 53003,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53003",
"DIIDBBGLDOL": "Bangboo_Name_53003",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 793541769103470830
},
{
"ID": 53004,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53004",
"DIIDBBGLDOL": "Bangboo_Name_53004",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 15333283710424173893
},
{
"ID": 53005,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53005",
"DIIDBBGLDOL": "Bangboo_Name_53005",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 6625485447473768309
},
{
"ID": 53006,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53006",
"DIIDBBGLDOL": "Bangboo_Name_53006",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 12516213533679239810
},
{
"ID": 53007,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53007",
"DIIDBBGLDOL": "Bangboo_Name_53007",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 1205030983673817181
},
{
"ID": 53008,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53008",
"DIIDBBGLDOL": "Bangboo_Name_53008",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 825481331324073038
},
{
"ID": 53009,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53009",
"DIIDBBGLDOL": "Bangboo_Name_53009",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 1109142289374885163
},
{
"ID": 53010,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53010",
"DIIDBBGLDOL": "Bangboo_Name_53010",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 7654740197435466664
},
{
"ID": 53011,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53011",
"DIIDBBGLDOL": "Bangboo_Name_53011",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 13043263780191768021
},
{
"ID": 53012,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_53012",
"DIIDBBGLDOL": "Bangboo_Name_53012",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 2166541009547495725
},
{
"ID": 54001,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54001",
"DIIDBBGLDOL": "Bangboo_Name_54001",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 12126171553945369848
},
{
"ID": 54002,
"BuddyType": 2,
"ECHNDHDIIAC": 3,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54002",
"DIIDBBGLDOL": "Bangboo_Name_54002",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 13558320022686804846
},
{
"ID": 54003,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54003",
"DIIDBBGLDOL": "Bangboo_Name_54003",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 9057893760084610210
},
{
"ID": 54004,
"BuddyType": 2,
"ECHNDHDIIAC": 2,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54004",
"DIIDBBGLDOL": "Bangboo_Name_54004",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 7098007717545413607
},
{
"ID": 54005,
"BuddyType": 2,
"ECHNDHDIIAC": 1,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54005",
"DIIDBBGLDOL": "Bangboo_Name_54005",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 7896901026978333696
},
{
"ID": 54006,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54006",
"DIIDBBGLDOL": "Bangboo_Name_54006",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 15413390933002967244
},
{
"ID": 54008,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54008",
"DIIDBBGLDOL": "Bangboo_Name_54008",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 13745481736816990381
},
{
"ID": 54009,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54009",
"DIIDBBGLDOL": "Bangboo_Name_54009",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 4565374391606395253
},
{
"ID": 54013,
"BuddyType": 2,
"ECHNDHDIIAC": 0,
"OHLKAFPBJHD": 0,
"GDDJBFHBJNK": 1,
"LAFKHMCKNIO": "Bangboo_Name_en_54013",
"DIIDBBGLDOL": "Bangboo_Name_54013",
"PCNEIBEDMCO": "BK_Eous",
"HIMPMHKGGIC": "",
"ANDDIMCDBME": 4076165347655962714
}
]

View file

@ -0,0 +1,519 @@
[
{
"RobotBuddyID": 19053001,
"BuddyID": 53001,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053001,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053002,
"BuddyID": 53002,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053002,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053003,
"BuddyID": 53003,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053003,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053004,
"BuddyID": 53004,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053004,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053005,
"BuddyID": 53005,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053005,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053006,
"BuddyID": 53006,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053006,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053007,
"BuddyID": 53007,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053007,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053008,
"BuddyID": 53008,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053008,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053009,
"BuddyID": 53009,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053009,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053010,
"BuddyID": 53010,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053010,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053011,
"BuddyID": 53011,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053011,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19053012,
"BuddyID": 53012,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1053012,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054001,
"BuddyID": 54001,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054001,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054002,
"BuddyID": 54002,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054002,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054003,
"BuddyID": 54003,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054003,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054004,
"BuddyID": 54004,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054004,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054005,
"BuddyID": 54005,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054005,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054006,
"BuddyID": 54006,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054006,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054008,
"BuddyID": 54008,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054008,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054009,
"BuddyID": 54009,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054009,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 19054013,
"BuddyID": 54013,
"CharLevel": 20,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 1054013,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 101000404,
"BuddyID": 53006,
"CharLevel": 1,
"CharUpgradeLevel": 1,
"CharStar": 1,
"GHGKBFEKDND": 53006,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 101000504,
"BuddyID": 54005,
"CharLevel": 6,
"CharUpgradeLevel": 1,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1000010104,
"BuddyID": 54005,
"CharLevel": 9,
"CharUpgradeLevel": 1,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1000010204,
"BuddyID": 54005,
"CharLevel": 9,
"CharUpgradeLevel": 1,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1000010304,
"BuddyID": 54005,
"CharLevel": 9,
"CharUpgradeLevel": 1,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1000010604,
"BuddyID": 54005,
"CharLevel": 11,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1000010704,
"BuddyID": 54005,
"CharLevel": 12,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1000010804,
"BuddyID": 54005,
"CharLevel": 13,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001000404,
"BuddyID": 54005,
"CharLevel": 8,
"CharUpgradeLevel": 1,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001010104,
"BuddyID": 54005,
"CharLevel": 10,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001010204,
"BuddyID": 54005,
"CharLevel": 15,
"CharUpgradeLevel": 2,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001020204,
"BuddyID": 54002,
"CharLevel": 24,
"CharUpgradeLevel": 3,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001020304,
"BuddyID": 54002,
"CharLevel": 24,
"CharUpgradeLevel": 3,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001020404,
"BuddyID": 54002,
"CharLevel": 26,
"CharUpgradeLevel": 3,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001020904,
"BuddyID": 54002,
"CharLevel": 28,
"CharUpgradeLevel": 3,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001021104,
"BuddyID": 54009,
"CharLevel": 32,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001021204,
"BuddyID": 54009,
"CharLevel": 33,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001030204,
"BuddyID": 54005,
"CharLevel": 34,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001030304,
"BuddyID": 54004,
"CharLevel": 35,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001030404,
"BuddyID": 54004,
"CharLevel": 36,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1001030504,
"BuddyID": 54004,
"CharLevel": 38,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1009010104,
"BuddyID": 53006,
"CharLevel": 25,
"CharUpgradeLevel": 3,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1009010204,
"BuddyID": 53006,
"CharLevel": 35,
"CharUpgradeLevel": 4,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1009010304,
"BuddyID": 53006,
"CharLevel": 45,
"CharUpgradeLevel": 5,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1009010404,
"BuddyID": 53006,
"CharLevel": 55,
"CharUpgradeLevel": 6,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
},
{
"RobotBuddyID": 1009010504,
"BuddyID": 53006,
"CharLevel": 60,
"CharUpgradeLevel": 6,
"CharStar": 1,
"GHGKBFEKDND": 0,
"ACGEEJMKBBO": 0,
"INPPGCADDPO": 0,
"FMPOMKKHELJ": 0
}
]

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@ use serde::Deserialize;
use super::BattleEventConfigID; use super::BattleEventConfigID;
template_id!(ArchiveBattleQuest u32 id); template_id!(ArchiveBattleQuest id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(ArchiveFileQuest u32 id); template_id!(ArchiveFileQuest id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(AvatarBase u32 id); template_id!(AvatarBase id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -2,7 +2,7 @@ use serde::Deserialize;
use super::OnceRewardID; use super::OnceRewardID;
template_id!(BattleEventConfig u32 id); template_id!(BattleEventConfig id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -2,7 +2,7 @@ use serde::Deserialize;
use super::BattleEventConfigID; use super::BattleEventConfigID;
template_id!(BattleGroupConfig u32 id); template_id!(BattleGroupConfig id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -0,0 +1,11 @@
use serde::Deserialize;
template_id!(BuddyBase id);
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct BuddyBaseTemplate {
#[serde(rename = "ID")]
pub id: BuddyBaseID,
pub buddy_type: u32,
}

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(HollowConfig u32 id); template_id!(HollowConfig id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(HollowQuest u32 id); template_id!(HollowQuest id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(Item u32 id); template_id!(Item id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(MainCityBgmConfig u32 id); template_id!(MainCityBgmConfig id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(MainCityDefaultObject u32 tag_id); template_id!(MainCityDefaultObject tag_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(MainCityObject u32 tag_id); template_id!(MainCityObject tag_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,29 +1,31 @@
use paste::paste; use paste::paste;
use std::sync::OnceLock; use std::sync::OnceLock;
use thiserror::Error;
use super::DataLoadError; use super::DataLoadError;
#[derive(Debug, Error)]
#[error("template with id {0} not found")]
pub struct TemplateNotFoundError(pub u32);
macro_rules! template_id { macro_rules! template_id {
($type_name:ident $underlying_type:ident $id_field:ident) => { ($type_name:ident $id_field:ident) => {
::paste::paste! { ::paste::paste! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ::serde::Deserialize, ::serde::Serialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ::serde::Deserialize, ::serde::Serialize)]
pub struct [<$type_name ID>]($underlying_type); pub struct [<$type_name ID>](u32);
impl [<$type_name ID>] { impl [<$type_name ID>] {
pub fn new(id: $underlying_type) -> Option<Self> { pub fn new(id: u32) -> Result<Self, super::TemplateNotFoundError> {
if crate::tables::[<$type_name:snake _template_tb>]::iter().any(|tmpl| tmpl.$id_field.value() == id) { crate::tables::[<$type_name:snake _template_tb>]::iter()
Some(Self(id)) .any(|tmpl| tmpl.$id_field.value() == id)
} .then_some(Self(id)).ok_or(super::TemplateNotFoundError(id))
else {
None
}
} }
pub fn new_unchecked(id: $underlying_type) -> Self { pub const fn new_unchecked(id: u32) -> Self {
Self(id) Self(id)
} }
pub fn value(&self) -> $underlying_type { pub fn value(&self) -> u32 {
self.0 self.0
} }
@ -76,6 +78,7 @@ macro_rules! template_tables {
template_tables! { template_tables! {
AvatarBaseTemplate; AvatarBaseTemplate;
BuddyBaseTemplate;
UnlockConfigTemplate; UnlockConfigTemplate;
SectionConfigTemplate; SectionConfigTemplate;
ProcedureConfigTemplate; ProcedureConfigTemplate;
@ -98,4 +101,6 @@ template_tables! {
QuickFuncTemplate; QuickFuncTemplate;
TeleportConfigTemplate; TeleportConfigTemplate;
ItemTemplate; ItemTemplate;
RobotConfigTemplate;
RobotBuddyConfigTemplate;
} }

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(OnceReward u32 reward_id); template_id!(OnceReward reward_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(PostGirlConfig u32 id); template_id!(PostGirlConfig id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(ProcedureConfig u32 procedure_id); template_id!(ProcedureConfig procedure_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(QuickAccess u32 quick_func_id); template_id!(QuickAccess quick_func_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(QuickFunc u32 btn_id); template_id!(QuickFunc btn_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -0,0 +1,17 @@
use serde::Deserialize;
use super::BuddyBaseID;
template_id!(RobotBuddyConfig robot_id);
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RobotBuddyConfigTemplate {
#[serde(rename = "RobotBuddyID")]
pub robot_id: RobotBuddyConfigID,
#[serde(rename = "BuddyID")]
pub buddy_id: BuddyBaseID,
pub char_level: u32,
pub char_upgrade_level: u32,
pub char_star: u32,
}

View file

@ -0,0 +1,24 @@
use serde::Deserialize;
use super::{AvatarBaseID, WeaponID};
template_id!(RobotConfig robot_id);
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RobotConfigTemplate {
#[serde(rename = "RobotID")]
pub robot_id: RobotConfigID,
#[serde(rename = "CharacterID")]
pub character_id: AvatarBaseID,
pub char_level: u32,
pub char_upgrade_level: u32,
pub char_star: u32,
pub skill_levels: Vec<u32>,
pub talent_level: u32,
#[serde(rename = "WeaponID")]
pub weapon_id: WeaponID,
pub weapon_level: u32,
pub weapon_upgrade_level: u32,
pub weapon_refine_level: u32,
}

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(SectionConfig u32 section_id); template_id!(SectionConfig section_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -2,7 +2,7 @@ use std::u32;
use serde::Deserialize; use serde::Deserialize;
template_id!(SubAreaData u32 area_id); template_id!(SubAreaData area_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(TeleportConfig i32 teleport_id); template_id!(TeleportConfig teleport_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -2,7 +2,7 @@ use serde::Deserialize;
use super::BattleEventConfigID; use super::BattleEventConfigID;
template_id!(TrainingQuest u32 id); template_id!(TrainingQuest id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(UnlockConfig i32 id); template_id!(UnlockConfig id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -2,7 +2,7 @@ use std::u32;
use serde::Deserialize; use serde::Deserialize;
template_id!(VariableData u32 id); template_id!(VariableData id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,6 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
template_id!(Weapon u32 item_id); template_id!(Weapon item_id);
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]

View file

@ -1,4 +1,4 @@
use data::tables::AvatarBaseID; use data::tables::{self, AvatarBaseID};
use proto::{AddAvatarPerformType, AddAvatarScNotify, PlayerSyncScNotify}; use proto::{AddAvatarPerformType, AddAvatarScNotify, PlayerSyncScNotify};
use crate::ServerState; use crate::ServerState;
@ -17,7 +17,7 @@ pub async fn add(
let uid = args[0].parse::<u32>()?; let uid = args[0].parse::<u32>()?;
let avatar_id = args[1].parse::<u32>()?; let avatar_id = args[1].parse::<u32>()?;
let Some(avatar_id) = AvatarBaseID::new(avatar_id) else { let Ok(avatar_id) = AvatarBaseID::new(avatar_id) else {
return Ok(format!("avatar with id {avatar_id} doesn't exist")); return Ok(format!("avatar with id {avatar_id} doesn't exist"));
}; };
@ -55,3 +55,62 @@ pub async fn add(
"successfully added avatar {avatar_id} to player {uid}" "successfully added avatar {avatar_id} to player {uid}"
)) ))
} }
pub async fn add_all(
args: ArgSlice<'_>,
state: &ServerState,
) -> Result<String, Box<dyn std::error::Error>> {
const USAGE: &str = "Usage: avatar add_all [player_uid]";
if args.len() != 1 {
return Ok(USAGE.to_string());
}
let uid = args[0].parse::<u32>()?;
let Some(player_lock) = state.player_mgr.get_player(uid).await else {
return Ok(String::from("player not found"));
};
let (session_id, avatar_sync, avatar_id_list) = {
let mut player = player_lock.lock().await;
let avatar_id_list = tables::avatar_base_template_tb::iter()
.filter(|tmpl| tmpl.id.value() < 2000 && !player.role_model.has_avatar(tmpl.id))
.map(|tmpl| tmpl.id)
.collect::<Vec<_>>();
avatar_id_list
.iter()
.for_each(|id| player.role_model.add_avatar(*id));
(
player.current_session_id(),
player.role_model.avatar_sync(),
avatar_id_list,
)
};
if let Some(session) = session_id.map(|id| state.session_mgr.get(id)).flatten() {
for id in avatar_id_list {
session
.notify(AddAvatarScNotify {
avatar_id: id.value(),
perform_type: AddAvatarPerformType::ShowPopup.into(),
..Default::default()
})
.await?;
}
session
.notify(PlayerSyncScNotify {
avatar: Some(avatar_sync),
..Default::default()
})
.await?;
} else {
state.player_mgr.save_and_remove(uid).await;
}
Ok(format!("successfully added all avatars to player {uid}"))
}

View file

@ -17,7 +17,7 @@ pub async fn add_weapon(
let uid = args[0].parse::<u32>()?; let uid = args[0].parse::<u32>()?;
let weapon_id = args[1].parse::<u32>()?; let weapon_id = args[1].parse::<u32>()?;
let Some(weapon_id) = WeaponID::new(weapon_id) else { let Ok(weapon_id) = WeaponID::new(weapon_id) else {
return Ok(format!("weapon with id {weapon_id} doesn't exist")); return Ok(format!("weapon with id {weapon_id} doesn't exist"));
}; };

View file

@ -86,9 +86,10 @@ impl CommandManager {
player::avatar "[player_uid] [avatar_id]" "changes player avatar for main city"; player::avatar "[player_uid] [avatar_id]" "changes player avatar for main city";
player::nickname "[player_uid] [nickname]" "changes player nickname"; player::nickname "[player_uid] [nickname]" "changes player nickname";
player::procedure "[player_uid] [procedure_id]" "changes current beginner procedure id, parameter -1 can be used for skipping it"; player::procedure "[player_uid] [procedure_id]" "changes current beginner procedure id, parameter -1 can be used for skipping it";
avatar::add "[player_uid] [avatar_id]" "gives avatar with specified id to player";
item::add_weapon "[player_uid] [weapon_id]" "gives weapon with specified id to player";
player::kick "[player_uid] [reason]" "kick the specified player (reason is optional)"; player::kick "[player_uid] [reason]" "kick the specified player (reason is optional)";
avatar::add "[player_uid] [avatar_id]" "gives avatar with specified id to player";
avatar::add_all "[player_uid]" "gives all avatars to player";
item::add_weapon "[player_uid] [weapon_id]" "gives weapon with specified id to player";
gacha::up "[player_uid]" "start a gacha UP setting guide (available for Bangboo pool)"; gacha::up "[player_uid]" "start a gacha UP setting guide (available for Bangboo pool)";
} }
} }

View file

@ -17,7 +17,7 @@ pub async fn avatar(
let uid = args[0].parse::<u32>()?; let uid = args[0].parse::<u32>()?;
let avatar_id = args[1].parse::<u32>()?; let avatar_id = args[1].parse::<u32>()?;
let Some(avatar_id) = AvatarBaseID::new(avatar_id) else { let Ok(avatar_id) = AvatarBaseID::new(avatar_id) else {
return Ok(format!("avatar with id {avatar_id} doesn't exist")); return Ok(format!("avatar with id {avatar_id} doesn't exist"));
}; };
@ -103,7 +103,7 @@ pub async fn procedure(
let procedure_id = args[1].parse::<i32>()?; let procedure_id = args[1].parse::<i32>()?;
let procedure_id = match procedure_id { let procedure_id = match procedure_id {
1.. => ProcedureConfigID::new(procedure_id as u32), 1.. => ProcedureConfigID::new(procedure_id as u32).ok(),
_ => None, _ => None,
}; };

View file

@ -1,8 +1,8 @@
use crate::logic::{EOperator, ESystem}; use crate::logic::{game::LogicError, EOperator, ESystem};
use super::*; use super::*;
use data::tables::{self, QuickFuncID}; use data::tables::{self, PostGirlConfigID, QuickFuncID};
pub async fn on_get_tips_info( pub async fn on_get_tips_info(
_session: &NetSession, _session: &NetSession,
@ -45,7 +45,7 @@ pub async fn on_get_client_systems_info(
teleport_data: Some(TeleportData { teleport_data: Some(TeleportData {
unlock_id_list: tables::teleport_config_template_tb::iter() unlock_id_list: tables::teleport_config_template_tb::iter()
.filter(|template| template.client_visible > 0) .filter(|template| template.client_visible > 0)
.map(|template| template.teleport_id.value()) .map(|template| template.teleport_id.value() as i32)
.collect(), .collect(),
..Default::default() ..Default::default()
}), }),
@ -196,22 +196,20 @@ pub async fn on_interact_with_scene_object(
} }
pub async fn on_mod_quick_menu( pub async fn on_mod_quick_menu(
_session: &NetSession, session: &NetSession,
_player: &mut Player, player: &mut Player,
_req: ModQuickMenuCsReq, req: ModQuickMenuCsReq,
) -> NetResult<ModQuickMenuScRsp> { ) -> NetResult<ModQuickMenuScRsp> {
let mut quick_access_data_list: Vec<QuickAccessData> = vec![]; req.quick_access_data_list.iter().for_each(|data| {
for data in _req.quick_access_data_list.iter() { player
quick_access_data_list.push(
_player
.lock_model .lock_model
.mod_quick_access(data.quick_access_index, QuickFuncID::new(data.btn_id)), .mod_quick_access(data.quick_access_index, QuickFuncID::new(data.btn_id).ok())
); });
}
_session session
.notify(PlayerSyncScNotify { .notify(PlayerSyncScNotify {
client_systems_sync: Some(ClientSystemsSync { client_systems_sync: Some(ClientSystemsSync {
quick_access_data_list: _player.lock_model.quick_access_to_client(), quick_access_data_list: player.lock_model.quick_access_to_client(),
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
@ -225,20 +223,19 @@ pub async fn on_mod_quick_menu(
} }
pub async fn on_change_post_girl( pub async fn on_change_post_girl(
_session: &NetSession, session: &NetSession,
_player: &mut Player, player: &mut Player,
_req: ChangePostGirlCsReq, req: ChangePostGirlCsReq,
) -> NetResult<ChangePostGirlScRsp> { ) -> NetResult<ChangePostGirlScRsp> {
if _req.new_selected_post_girl_id_list.len() != 1 { let post_girl_id = *req
return Ok(ChangePostGirlScRsp { .new_selected_post_girl_id_list
retcode: Retcode::RetFail.into(), .get(0)
..Default::default() .ok_or(Retcode::RetFail)?;
});
}; let post_girl_id = PostGirlConfigID::new(post_girl_id).map_err(LogicError::from)?;
match tables::PostGirlConfigID::new(*_req.new_selected_post_girl_id_list.get(0).unwrap()) {
Some(post_girl_id) => { player.basic_data_model.selected_post_girl_id = Some(post_girl_id);
_player.basic_data_model.selected_post_girl_id = Some(post_girl_id); session
_session
.notify(PlayerSyncScNotify { .notify(PlayerSyncScNotify {
client_systems_sync: Some(ClientSystemsSync { client_systems_sync: Some(ClientSystemsSync {
post_girl_data: Some(PostGirlSync { post_girl_data: Some(PostGirlSync {
@ -250,14 +247,9 @@ pub async fn on_change_post_girl(
..Default::default() ..Default::default()
}) })
.await?; .await?;
Ok(ChangePostGirlScRsp { Ok(ChangePostGirlScRsp {
retcode: Retcode::RetSucc.into(), retcode: Retcode::RetSucc.into(),
..Default::default() ..Default::default()
}) })
} }
None => Ok(ChangePostGirlScRsp {
retcode: Retcode::RetFail.into(),
..Default::default()
}),
}
}

View file

@ -123,7 +123,7 @@ pub async fn on_gacha_free_agent(
)?; )?;
let item_id = ItemID::new(req.avatar_id); let item_id = ItemID::new(req.avatar_id);
if let None = item_id { if item_id.is_err() {
return Err(NetError::from(Retcode::RetFail)); return Err(NetError::from(Retcode::RetFail));
} }
let item_id = item_id.unwrap(); let item_id = item_id.unwrap();
@ -157,7 +157,7 @@ pub async fn on_choose_gacha_up(
)?; )?;
let item_id = ItemID::new(req.item_id); let item_id = ItemID::new(req.item_id);
if let None = item_id { if item_id.is_err() {
return Err(NetError::from(Retcode::RetFail)); return Err(NetError::from(Retcode::RetFail));
} }
let item_id = item_id.unwrap(); let item_id = item_id.unwrap();
@ -200,18 +200,18 @@ fn add_item(
) -> NetResult<u32> { ) -> NetResult<u32> {
match item_type { match item_type {
GachaAddedItemType::Character => match AvatarBaseID::new(item_id.value()) { GachaAddedItemType::Character => match AvatarBaseID::new(item_id.value()) {
Some(avatar_id) => { Ok(avatar_id) => {
role_model.add_avatar(avatar_id); role_model.add_avatar(avatar_id);
Ok(0) Ok(0)
} }
None => { Err(_) => {
tracing::info!("add item failed for avatar id {item_id}"); tracing::info!("add item failed for avatar id {item_id}");
Err(NetError::from(Retcode::RetFail)) Err(NetError::from(Retcode::RetFail))
} }
}, },
GachaAddedItemType::Weapon => match WeaponID::new(item_id.value()) { GachaAddedItemType::Weapon => match WeaponID::new(item_id.value()) {
Some(weapon_id) => Ok(item_model.add_weapon(weapon_id).value()), Ok(weapon_id) => Ok(item_model.add_weapon(weapon_id).value()),
None => { Err(_) => {
tracing::info!("add item failed for weapon id {item_id}"); tracing::info!("add item failed for weapon id {item_id}");
Err(NetError::from(Retcode::RetFail)) Err(NetError::from(Retcode::RetFail))
} }

View file

@ -1,5 +1,7 @@
use data::tables::AvatarBaseID; use data::tables::AvatarBaseID;
use crate::logic::game::LogicError;
use super::*; use super::*;
pub async fn on_get_item_data( pub async fn on_get_item_data(
@ -23,7 +25,7 @@ pub async fn on_weapon_dress(
req: WeaponDressCsReq, req: WeaponDressCsReq,
) -> NetResult<WeaponDressScRsp> { ) -> NetResult<WeaponDressScRsp> {
player.dress_weapon( player.dress_weapon(
AvatarBaseID::new(req.avatar_id).ok_or(Retcode::RetFail)?, AvatarBaseID::new(req.avatar_id).map_err(LogicError::from)?,
req.weapon_uid.into(), req.weapon_uid.into(),
)?; )?;
@ -45,7 +47,7 @@ pub async fn on_weapon_un_dress(
player: &mut Player, player: &mut Player,
req: WeaponUnDressCsReq, req: WeaponUnDressCsReq,
) -> NetResult<WeaponUnDressScRsp> { ) -> NetResult<WeaponUnDressScRsp> {
let avatar_id = AvatarBaseID::new(req.avatar_id).ok_or(Retcode::RetFail)?; let avatar_id = AvatarBaseID::new(req.avatar_id).map_err(LogicError::from)?;
let avatar = player let avatar = player
.role_model .role_model
.avatar_list .avatar_list

View file

@ -22,7 +22,7 @@ pub async fn on_create_role(
player: &mut Player, player: &mut Player,
req: CreateRoleCsReq, req: CreateRoleCsReq,
) -> NetResult<CreateRoleScRsp> { ) -> NetResult<CreateRoleScRsp> {
let avatar_id = AvatarBaseID::new(req.avatar_id).ok_or(Retcode::RetFail)?; let avatar_id = AvatarBaseID::new(req.avatar_id).map_err(LogicError::from)?;
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else { let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail)); return Err(NetError::from(Retcode::RetFail));

View file

@ -88,7 +88,7 @@ pub async fn on_begin_archive_battle_quest(
player: &mut Player, player: &mut Player,
req: BeginArchiveBattleQuestCsReq, req: BeginArchiveBattleQuestCsReq,
) -> NetResult<BeginArchiveBattleQuestScRsp> { ) -> NetResult<BeginArchiveBattleQuestScRsp> {
let quest_id = ArchiveBattleQuestID::new(req.quest_id).ok_or(Retcode::RetFail)?; let quest_id = ArchiveBattleQuestID::new(req.quest_id).map_err(LogicError::from)?;
player.game_instance = GameInstance::Hollow( player.game_instance = GameInstance::Hollow(
HollowGame::create_archive_battle( HollowGame::create_archive_battle(

View file

@ -47,7 +47,7 @@ pub async fn on_advance_beginner_procedure(
}; };
let procedure_id = let procedure_id =
ProcedureConfigID::new(req.procedure_id as u32).ok_or(Retcode::RetFail)?; ProcedureConfigID::new(req.procedure_id as u32).map_err(LogicError::from)?;
fresh_game fresh_game
.procedure_mgr .procedure_mgr
@ -168,7 +168,7 @@ pub async fn on_start_trial_fighting_mission(
player: &mut Player, player: &mut Player,
req: StartTrialFightingMissionCsReq, req: StartTrialFightingMissionCsReq,
) -> NetResult<StartTrialFightingMissionScRsp> { ) -> NetResult<StartTrialFightingMissionScRsp> {
let quest_id = TrainingQuestID::new(req.quest_id).ok_or(Retcode::RetFail)?; let quest_id = TrainingQuestID::new(req.quest_id).map_err(LogicError::from)?;
player.game_instance = GameInstance::Hollow( player.game_instance = GameInstance::Hollow(
HollowGame::create_training_game(quest_id, ELocalPlayType::TrainingRoomFight, &req.avatars) HollowGame::create_training_game(quest_id, ELocalPlayType::TrainingRoomFight, &req.avatars)
@ -273,7 +273,7 @@ pub async fn on_enter_section(
player: &mut Player, player: &mut Player,
req: EnterSectionCsReq, req: EnterSectionCsReq,
) -> NetResult<EnterSectionScRsp> { ) -> NetResult<EnterSectionScRsp> {
let section_id = SectionConfigID::new(req.section_id).ok_or(Retcode::RetFail)?; let section_id = SectionConfigID::new(req.section_id).map_err(LogicError::from)?;
player.main_city_model.switch_section(section_id); player.main_city_model.switch_section(section_id);
player.init_frontend_game()?; player.init_frontend_game()?;
@ -311,7 +311,7 @@ pub async fn on_start_hollow_quest(
) -> NetResult<StartHollowQuestScRsp> { ) -> NetResult<StartHollowQuestScRsp> {
use crate::logic::{TimePeriodType, WeatherType}; use crate::logic::{TimePeriodType, WeatherType};
let quest_id = HollowQuestID::new(req.quest_id).ok_or(Retcode::RetFail)?; let quest_id = HollowQuestID::new(req.quest_id).map_err(LogicError::from)?;
let quest_type = EHollowQuestType::from(quest_id.template().hollow_quest_type); let quest_type = EHollowQuestType::from(quest_id.template().hollow_quest_type);
match quest_type { match quest_type {

View file

@ -2,17 +2,17 @@ use std::collections::HashMap;
use crate::logic::BuddyTeamType; use crate::logic::BuddyTeamType;
use super::unit::{AvatarUnit, BuddyUnit}; use super::unit::{AvatarUnit, AvatarUnitID, BuddyUnit, BuddyUnitID};
pub struct TeamDataItem { pub struct TeamDataItem {
pub avatar_member_list: Vec<AvatarUnit>, pub avatar_member_list: Vec<AvatarUnit>,
pub equipped_buddy_list: Vec<BuddyUnit>, pub equipped_buddy_list: Vec<BuddyUnit>,
} }
pub struct BuddyParam(pub u32, pub BuddyTeamType); pub struct BuddyParam(pub BuddyUnitID, pub BuddyTeamType);
impl TeamDataItem { impl TeamDataItem {
pub fn new(avatars: &[u32], buddy_params: &[BuddyParam]) -> Self { pub fn new(avatars: &[AvatarUnitID], buddy_params: &[BuddyParam]) -> Self {
Self { Self {
avatar_member_list: avatars avatar_member_list: avatars
.iter() .iter()

View file

@ -1,17 +1,36 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::logic::BaseProperty; use crate::logic::BaseProperty;
use data::tables::{AvatarBaseID, RobotConfigID};
use proto::AvatarUnitInfo; use proto::AvatarUnitInfo;
pub struct AvatarUnit { pub struct AvatarUnit {
pub avatar_id: u32, pub avatar_id: AvatarUnitID,
pub mp_property_override: HashMap<BaseProperty, i32>, pub mp_property_override: HashMap<BaseProperty, i32>,
} }
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AvatarUnitID {
Base(AvatarBaseID),
Robot(RobotConfigID),
}
impl AvatarUnitID {
pub fn base_id(&self) -> AvatarBaseID {
match *self {
Self::Base(id) => id,
Self::Robot(id) => id.template().character_id,
}
}
}
impl AvatarUnit { impl AvatarUnit {
pub fn to_client(&self) -> AvatarUnitInfo { pub fn to_client(&self) -> AvatarUnitInfo {
AvatarUnitInfo { AvatarUnitInfo {
avatar_id: self.avatar_id, avatar_id: match self.avatar_id {
AvatarUnitID::Base(id) => id.value(),
AvatarUnitID::Robot(id) => id.value(),
},
mp_property_override_map: self mp_property_override_map: self
.mp_property_override .mp_property_override
.iter() .iter()

View file

@ -1,19 +1,38 @@
use std::collections::HashMap; use std::collections::HashMap;
use data::tables::{BuddyBaseID, RobotBuddyConfigID};
use proto::BuddyUnitInfo; use proto::BuddyUnitInfo;
use crate::logic::{BaseProperty, BuddyTeamType}; use crate::logic::{BaseProperty, BuddyTeamType};
pub struct BuddyUnit { pub struct BuddyUnit {
pub buddy_id: u32, pub buddy_id: BuddyUnitID,
pub buddy_team: BuddyTeamType, pub buddy_team: BuddyTeamType,
pub override_property_map: HashMap<BaseProperty, i32>, pub override_property_map: HashMap<BaseProperty, i32>,
} }
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BuddyUnitID {
Base(BuddyBaseID),
Robot(RobotBuddyConfigID),
}
impl BuddyUnitID {
pub fn base_id(&self) -> BuddyBaseID {
match *self {
Self::Base(id) => id,
Self::Robot(id) => id.template().buddy_id,
}
}
}
impl BuddyUnit { impl BuddyUnit {
pub fn to_client(&self) -> BuddyUnitInfo { pub fn to_client(&self) -> BuddyUnitInfo {
BuddyUnitInfo { BuddyUnitInfo {
buddy_id: self.buddy_id, buddy_id: match self.buddy_id {
BuddyUnitID::Base(id) => id.value(),
BuddyUnitID::Robot(id) => id.value(),
},
r#type: self.buddy_team.to_protocol().into(), r#type: self.buddy_team.to_protocol().into(),
mp_property_override_map: self mp_property_override_map: self
.override_property_map .override_property_map

View file

@ -1,5 +1,5 @@
mod avatar; mod avatar;
mod buddy; mod buddy;
pub use avatar::AvatarUnit; pub use avatar::{AvatarUnit, AvatarUnitID};
pub use buddy::BuddyUnit; pub use buddy::{BuddyUnit, BuddyUnitID};

View file

@ -509,7 +509,7 @@ fn determine_gacha_result<'bin, 'conf>(
// TODO: apply_on_owned_count in a context with bag // TODO: apply_on_owned_count in a context with bag
// TODO: That's what RoleModel should do, not me. // TODO: That's what RoleModel should do, not me.
if extra_items_policy.apply_on_owned_count == 0 { if extra_items_policy.apply_on_owned_count == 0 {
extra_item_id = ItemID::new(extra_items_policy.id); extra_item_id = ItemID::new(extra_items_policy.id).ok();
extra_item_count = extra_items_policy.count; extra_item_count = extra_items_policy.count;
} }
} }

View file

@ -19,8 +19,8 @@ impl GachaExtraResources {
Some(bin) => { Some(bin) => {
let item_id_opt = ItemID::new(bin.extra_item_id); let item_id_opt = ItemID::new(bin.extra_item_id);
match item_id_opt { match item_id_opt {
None => None, Err(_) => None,
Some(extra_item_id) => Some(Self { Ok(extra_item_id) => Some(Self {
extra_item_id, extra_item_id,
extra_item_count: bin.extra_item_count, extra_item_count: bin.extra_item_count,
}), }),

View file

@ -1,6 +1,7 @@
use common::util; use common::util;
use data::tables::{ use data::tables::{
self, ArchiveBattleQuestID, BattleEventConfigID, HollowQuestID, TrainingQuestID, self, ArchiveBattleQuestID, AvatarBaseID, BattleEventConfigID, BuddyBaseID, HollowQuestID,
RobotBuddyConfigID, RobotConfigID, TrainingQuestID,
}; };
use proto::{DungeonInfo, DungeonItemData, FightSceneInfo, SceneInfo, WeatherPoolInfo}; use proto::{DungeonInfo, DungeonItemData, FightSceneInfo, SceneInfo, WeatherPoolInfo};
use thiserror::Error; use thiserror::Error;
@ -8,7 +9,7 @@ use thiserror::Error;
use crate::logic::{ use crate::logic::{
battle::{ battle::{
drop::FightDropPool, drop::FightDropPool,
unit::{AvatarUnit, BuddyUnit}, unit::{AvatarUnit, AvatarUnitID, BuddyUnit, BuddyUnitID},
BuddyParam, DungeonQuestManager, TeamDataItem, BuddyParam, DungeonQuestManager, TeamDataItem,
}, },
BuddyTeamType, EHollowQuestType, ELocalPlayType, ESceneType, TimePeriodType, WeatherType, BuddyTeamType, EHollowQuestType, ELocalPlayType, ESceneType, TimePeriodType, WeatherType,
@ -22,6 +23,10 @@ pub enum HollowGameError {
QuestTypeNotSupported(u32, EHollowQuestType), QuestTypeNotSupported(u32, EHollowQuestType),
#[error("Battle group not found, quest id: {0}")] #[error("Battle group not found, quest id: {0}")]
BattleGroupNotFound(u32), BattleGroupNotFound(u32),
#[error("Invalid avatar id: {0}")]
InvalidAvatarID(u32),
#[error("Invalid robot id: {0}")]
InvalidRobotID(u32),
} }
pub struct HollowGame { pub struct HollowGame {
@ -44,6 +49,15 @@ impl HollowGame {
) -> Result<Self, HollowGameError> { ) -> Result<Self, HollowGameError> {
let template = training_quest_id.template(); let template = training_quest_id.template();
let avatars = match avatars
.iter()
.map(|id| AvatarBaseID::new(*id).map(|id| AvatarUnitID::Base(id)))
.collect::<Result<Vec<_>, _>>()
{
Ok(avatars) => avatars,
Err(err) => return Err(HollowGameError::InvalidAvatarID(err.0)),
};
Ok(Self { Ok(Self {
quest_id: template.id.value(), quest_id: template.id.value(),
battle_event_id: template.battle_event_id, battle_event_id: template.battle_event_id,
@ -51,7 +65,7 @@ impl HollowGame {
weather: WeatherType::SunShine, weather: WeatherType::SunShine,
start_timestamp: util::cur_timestamp() as i64, start_timestamp: util::cur_timestamp() as i64,
play_type, play_type,
team_data: TeamDataItem::new(avatars, &[]), team_data: TeamDataItem::new(&avatars, &[]),
fight_drop_pool: FightDropPool::new(template.battle_event_id), fight_drop_pool: FightDropPool::new(template.battle_event_id),
quest_manager: DungeonQuestManager::default(), quest_manager: DungeonQuestManager::default(),
}) })
@ -65,6 +79,20 @@ impl HollowGame {
) -> Result<Self, HollowGameError> { ) -> Result<Self, HollowGameError> {
let template = archive_battle_quest_id.template(); let template = archive_battle_quest_id.template();
let avatars = match avatars
.iter()
.map(|id| RobotConfigID::new(*id).map(|id| AvatarUnitID::Robot(id)))
.collect::<Result<Vec<_>, _>>()
{
Ok(avatars) => avatars,
Err(err) => return Err(HollowGameError::InvalidAvatarID(err.0)),
};
let buddy_params = match RobotBuddyConfigID::new(buddy_id) {
Ok(id) => vec![BuddyParam(BuddyUnitID::Robot(id), BuddyTeamType::Fighting)],
Err(_) => Vec::with_capacity(0),
};
Ok(Self { Ok(Self {
quest_id: template.id.value(), quest_id: template.id.value(),
battle_event_id: template.battle_event_id, battle_event_id: template.battle_event_id,
@ -72,12 +100,7 @@ impl HollowGame {
weather: WeatherType::SunShine, weather: WeatherType::SunShine,
start_timestamp: util::cur_timestamp() as i64, start_timestamp: util::cur_timestamp() as i64,
play_type, play_type,
team_data: TeamDataItem::new( team_data: TeamDataItem::new(&avatars, &buddy_params),
avatars,
&(buddy_id != 0)
.then_some(vec![BuddyParam(buddy_id, BuddyTeamType::Fighting)])
.unwrap_or_default(),
),
fight_drop_pool: FightDropPool::new(template.battle_event_id), fight_drop_pool: FightDropPool::new(template.battle_event_id),
quest_manager: DungeonQuestManager::default(), quest_manager: DungeonQuestManager::default(),
}) })
@ -104,6 +127,20 @@ impl HollowGame {
return Err(HollowGameError::BattleGroupNotFound(template.id.value())); return Err(HollowGameError::BattleGroupNotFound(template.id.value()));
}; };
let avatars = match avatars
.iter()
.map(|id| AvatarBaseID::new(*id).map(|id| AvatarUnitID::Base(id)))
.collect::<Result<Vec<_>, _>>()
{
Ok(avatars) => avatars,
Err(err) => return Err(HollowGameError::InvalidAvatarID(err.0)),
};
let buddy_params = match BuddyBaseID::new(buddy_id) {
Ok(id) => vec![BuddyParam(BuddyUnitID::Base(id), BuddyTeamType::Fighting)],
Err(_) => Vec::with_capacity(0),
};
Ok(Self { Ok(Self {
quest_id: template.id.value(), quest_id: template.id.value(),
battle_event_id: battle_group.battle_event_id, battle_event_id: battle_group.battle_event_id,
@ -113,12 +150,7 @@ impl HollowGame {
play_type: Self::get_play_type_by_quest_type(EHollowQuestType::from( play_type: Self::get_play_type_by_quest_type(EHollowQuestType::from(
template.hollow_quest_type, template.hollow_quest_type,
)), )),
team_data: TeamDataItem::new( team_data: TeamDataItem::new(&avatars, &buddy_params),
avatars,
&(buddy_id != 0)
.then_some(vec![BuddyParam(buddy_id, BuddyTeamType::Fighting)])
.unwrap_or_default(),
),
fight_drop_pool: FightDropPool::new(battle_group.battle_event_id), fight_drop_pool: FightDropPool::new(battle_group.battle_event_id),
quest_manager: DungeonQuestManager::new_for_battle_group(battle_group.id), quest_manager: DungeonQuestManager::new_for_battle_group(battle_group.id),
}) })

View file

@ -1,5 +1,5 @@
use common::util; use common::util;
use data::tables::{self, BattleEventConfigID, HollowQuestID}; use data::tables::{self, AvatarBaseID, BattleEventConfigID, BuddyBaseID, HollowQuestID};
use proto::{ use proto::{
DungeonInfo, DungeonItemData, FightQuestInfo, LongFightInfo, LongFightSceneInfo, SceneInfo, DungeonInfo, DungeonItemData, FightQuestInfo, LongFightInfo, LongFightSceneInfo, SceneInfo,
WeatherPoolInfo, WeatherPoolInfo,
@ -9,7 +9,7 @@ use thiserror::Error;
use crate::logic::{ use crate::logic::{
battle::{ battle::{
drop::FightDropPool, drop::FightDropPool,
unit::{AvatarUnit, BuddyUnit}, unit::{AvatarUnit, AvatarUnitID, BuddyUnit, BuddyUnitID},
BuddyParam, DungeonQuestManager, LogicVariableTable, TeamDataItem, BuddyParam, DungeonQuestManager, LogicVariableTable, TeamDataItem,
}, },
BuddyTeamType, EHollowQuestType, ELocalPlayType, ESceneType, TimePeriodType, WeatherType, BuddyTeamType, EHollowQuestType, ELocalPlayType, ESceneType, TimePeriodType, WeatherType,
@ -23,6 +23,8 @@ pub enum LongFightGameError {
InvalidQuestType(EHollowQuestType), InvalidQuestType(EHollowQuestType),
#[error("Battle group not found, quest id: {0}")] #[error("Battle group not found, quest id: {0}")]
BattleGroupNotFound(u32), BattleGroupNotFound(u32),
#[error("Invalid avatar id: {0}")]
InvalidAvatarID(u32),
} }
pub struct LongFightGame { pub struct LongFightGame {
@ -39,7 +41,7 @@ pub struct LongFightGame {
} }
impl LongFightGame { impl LongFightGame {
const RALLY_GUIDANCE_BUDDY_ID: u32 = 50001; const RALLY_GUIDANCE_BUDDY: BuddyBaseID = BuddyBaseID::new_unchecked(50001);
pub fn create_rally_game( pub fn create_rally_game(
quest_id: HollowQuestID, quest_id: HollowQuestID,
@ -61,13 +63,25 @@ impl LongFightGame {
return Err(LongFightGameError::BattleGroupNotFound(template.id.value())); return Err(LongFightGameError::BattleGroupNotFound(template.id.value()));
}; };
let avatars = match avatars
.iter()
.map(|id| AvatarBaseID::new(*id).map(|id| AvatarUnitID::Base(id)))
.collect::<Result<Vec<_>, _>>()
{
Ok(avatars) => avatars,
Err(err) => return Err(LongFightGameError::InvalidAvatarID(err.0)),
};
let mut buddy_params = vec![BuddyParam( let mut buddy_params = vec![BuddyParam(
Self::RALLY_GUIDANCE_BUDDY_ID, BuddyUnitID::Base(Self::RALLY_GUIDANCE_BUDDY),
BuddyTeamType::RallyGuidance, BuddyTeamType::RallyGuidance,
)]; )];
if buddy_id != 0 { if let Ok(buddy_id) = BuddyBaseID::new(buddy_id) {
buddy_params.push(BuddyParam(buddy_id, BuddyTeamType::Fighting)); buddy_params.push(BuddyParam(
BuddyUnitID::Base(buddy_id),
BuddyTeamType::Fighting,
));
} }
Ok(Self { Ok(Self {
@ -77,7 +91,7 @@ impl LongFightGame {
time_period, time_period,
weather, weather,
start_timestamp: util::cur_timestamp() as i64, start_timestamp: util::cur_timestamp() as i64,
team_data: TeamDataItem::new(avatars, &buddy_params), team_data: TeamDataItem::new(&avatars, &buddy_params),
variable_table: LogicVariableTable::new(battle_group.battle_event_id), variable_table: LogicVariableTable::new(battle_group.battle_event_id),
fight_drop_pool: FightDropPool::new(battle_group.battle_event_id), fight_drop_pool: FightDropPool::new(battle_group.battle_event_id),
quest_manager: DungeonQuestManager::new_for_battle_group(battle_group.id), quest_manager: DungeonQuestManager::new_for_battle_group(battle_group.id),

View file

@ -2,6 +2,7 @@ mod fresh;
mod frontend; mod frontend;
mod hollow; mod hollow;
mod long_fight; mod long_fight;
use data::tables::TemplateNotFoundError;
pub use fresh::*; pub use fresh::*;
pub use frontend::*; pub use frontend::*;
pub use hollow::*; pub use hollow::*;
@ -36,6 +37,8 @@ pub enum LogicError {
LongFight(#[from] LongFightGameError), LongFight(#[from] LongFightGameError),
#[error("dungeon quest error: {0}")] #[error("dungeon quest error: {0}")]
DungeonQuest(#[from] DungeonQuestError), DungeonQuest(#[from] DungeonQuestError),
#[error("{0}")]
TemplateNotFound(#[from] TemplateNotFoundError),
} }
impl GameInstance { impl GameInstance {

View file

@ -53,14 +53,14 @@ impl BasicDataModel {
exp: bin.exp, exp: bin.exp,
profile_icon: bin.profile_icon, profile_icon: bin.profile_icon,
frontend_avatar_id: match bin.frontend_avatar_id { frontend_avatar_id: match bin.frontend_avatar_id {
1.. => AvatarBaseID::new(bin.frontend_avatar_id as u32), 1.. => AvatarBaseID::new(bin.frontend_avatar_id as u32).ok(),
_ => None, _ => None,
}, },
beginner_procedure_id: match bin.beginner_procedure_id { beginner_procedure_id: match bin.beginner_procedure_id {
1.. => ProcedureConfigID::new(bin.beginner_procedure_id as u32), 1.. => ProcedureConfigID::new(bin.beginner_procedure_id as u32).ok(),
_ => None, _ => None,
}, },
selected_post_girl_id: PostGirlConfigID::new(bin.selected_post_girl_id), selected_post_girl_id: PostGirlConfigID::new(bin.selected_post_girl_id).ok(),
nick_name: match bin.nick_name.is_empty() { nick_name: match bin.nick_name.is_empty() {
true => None, true => None,
false => Some(bin.nick_name), false => Some(bin.nick_name),
@ -84,7 +84,7 @@ impl BasicDataModel {
.unwrap_or(-1), .unwrap_or(-1),
selected_post_girl_id: match self.selected_post_girl_id { selected_post_girl_id: match self.selected_post_girl_id {
Some(post_girl_id) => post_girl_id.value(), Some(post_girl_id) => post_girl_id.value(),
None => 0 None => 0,
}, },
} }
} }

View file

@ -15,7 +15,7 @@ impl LockModel {
unlock_list: bin unlock_list: bin
.unlock_list .unlock_list
.into_iter() .into_iter()
.map(UnlockConfigID::new_unchecked) .map(|id| UnlockConfigID::new_unchecked(id as u32))
.collect(), .collect(),
quick_access_list: bin quick_access_list: bin
.quick_access_list .quick_access_list
@ -31,7 +31,7 @@ impl LockModel {
.unlock_list .unlock_list
.clone() .clone()
.into_iter() .into_iter()
.map(|i| i.value()) .map(|i| i.value() as i32)
.collect(), .collect(),
quick_access_list: self quick_access_list: self
.quick_access_list .quick_access_list
@ -65,13 +65,12 @@ impl LockModel {
} }
pub fn to_client(&self) -> UnlockData { pub fn to_client(&self) -> UnlockData {
UnlockData { UnlockData {
unlock_id_list: self unlock_id_list: self
.unlock_list .unlock_list
.clone() .clone()
.into_iter() .into_iter()
.map(|i| i.value()) .map(|i| i.value() as i32)
.collect(), .collect(),
quick_access_data_list: self.quick_access_to_client(), quick_access_data_list: self.quick_access_to_client(),
..Default::default() ..Default::default()
@ -86,27 +85,10 @@ impl LockModel {
self.unlock_list.contains(&id) self.unlock_list.contains(&id)
} }
pub fn mod_quick_access(&mut self, index: u32, id: Option<QuickFuncID>) -> QuickAccessData { pub fn mod_quick_access(&mut self, index: u32, id: Option<QuickFuncID>) {
let btn_id = match id { match id {
Some(quick_access_id) => { Some(quick_access_id) => self.quick_access_list.insert(index, quick_access_id),
if self.quick_access_list.contains_key(&index) { None => self.quick_access_list.remove(&index),
*self.quick_access_list.get_mut(&index).unwrap() = quick_access_id;
} else {
self.quick_access_list.insert(index, quick_access_id);
}
quick_access_id.value()
}
None => {
if self.quick_access_list.contains_key(&index) {
self.quick_access_list.remove(&index);
}
0
}
}; };
QuickAccessData {
r#type: QuickAccessType::QuickMenu.into(),
quick_access_index: index,
btn_id,
}
} }
} }

View file

@ -22,15 +22,17 @@ impl RoleModel {
const DEFAULT_AVATARS: [u32; 2] = [1011, 1081]; const DEFAULT_AVATARS: [u32; 2] = [1011, 1081];
pub fn add_avatar(&mut self, template_id: AvatarBaseID) { pub fn add_avatar(&mut self, template_id: AvatarBaseID) {
if !self if !self.has_avatar(template_id) {
.avatar_list
.iter()
.any(|a| a.template_id == template_id)
{
self.avatar_list.push(Avatar::new(template_id)); self.avatar_list.push(Avatar::new(template_id));
} }
} }
pub fn has_avatar(&self, template_id: AvatarBaseID) -> bool {
self.avatar_list
.iter()
.any(|a| a.template_id == template_id)
}
pub fn avatar_sync(&self) -> AvatarSync { pub fn avatar_sync(&self) -> AvatarSync {
AvatarSync { AvatarSync {
avatar_list: self.avatar_list.iter().map(Avatar::to_client).collect(), avatar_list: self.avatar_list.iter().map(Avatar::to_client).collect(),

View file

@ -12,7 +12,7 @@ impl SceneUnitManager {
let unit_vec = tables::main_city_object_template_tb::iter() let unit_vec = tables::main_city_object_template_tb::iter()
.filter(|tmpl| tmpl.get_section_name() == section_template.section_name) .filter(|tmpl| tmpl.get_section_name() == section_template.section_name)
.filter(|tmpl| MainCityDefaultObjectID::new(tmpl.tag_id.value()).is_some()) // check if npc tag present in default object table .filter(|tmpl| MainCityDefaultObjectID::new(tmpl.tag_id.value()).is_ok()) // check if npc tag present in default object table
.map(|tmpl| SceneUnit::new(tmpl.tag_id)) .map(|tmpl| SceneUnit::new(tmpl.tag_id))
.collect(); .collect();