diff --git a/AquaNet/src/components/UserBox.svelte b/AquaNet/src/components/UserBox.svelte
deleted file mode 100644
index a2be5cc3..00000000
--- a/AquaNet/src/components/UserBox.svelte
+++ /dev/null
@@ -1,552 +0,0 @@
-
-
-
-
-{#if !loading && !error}
-
- {#each userBoxFields as { key, label, kind }, i (key)}
-
-
-
-
- {#if changed.includes(key)}
-
- {/if}
-
-
- {/each}
-
- {#if HAS_USERBOX_ASSETS}
-
-
{t("userbox.preview.ui")}
-
- {#if values.frame}
-

- {/if}
-
-
-
- {#if values.mapicon}
-
-

-
- {/if}
-
-
- {#if values.voice}
-
-

-
- {/if}
-
-
-
{t("userbox.preview.nameplate")}
-
- {#if values.nameplate}
-
-

-
- {availableOptions.trophy.find((x) => x.id === values.trophy)
- ?.label}
-
-
-
- {user.displayName}
-
-
-
- {/if}
-
-
{t("userbox.preview.avatar")}
-
-
-

-
-
-

-
-
-

-
-
-

-
-
-

-
-
-

-
-
-

-
-
-
- {/if}
-{/if}
-
-
-
diff --git a/AquaNet/src/components/settings/ChuniSettings.svelte b/AquaNet/src/components/settings/ChuniSettings.svelte
new file mode 100644
index 00000000..63a7739b
--- /dev/null
+++ b/AquaNet/src/components/settings/ChuniSettings.svelte
@@ -0,0 +1,205 @@
+
+
+
+
+
+{#if !loading && !error}
+
+
{t("userbox.header.general")}
+
+
{t("userbox.header.userbox")}
+
+ {#each userItems as { iKey, ubKey, items }, i}
+
+
+
+
+ {#if changed.includes(ubKey)}
+
+ {/if}
+
+
+ {/each}
+
+ {#if HAS_USERBOX_ASSETS}
+
{t("userbox.header.preview")}
+
{t("userbox.preview.notice")}
+
+ {#if preview}
+
+ {#each userItems.filter(v => v.iKey != 'trophy' && v.iKey != 'systemVoice') as { iKey, ubKey, items }, i}
+
+
{ts(`userbox.${ubKey}`)}
+
.padStart(8,)
+
+ {/each}
+
+ {/if}
+ {/if}
+
+{/if}
+
+
diff --git a/AquaNet/src/components/GameSettingFields.svelte b/AquaNet/src/components/settings/GameSettingFields.svelte
similarity index 87%
rename from AquaNet/src/components/GameSettingFields.svelte
rename to AquaNet/src/components/settings/GameSettingFields.svelte
index 4364e181..8d5e09ad 100644
--- a/AquaNet/src/components/GameSettingFields.svelte
+++ b/AquaNet/src/components/settings/GameSettingFields.svelte
@@ -1,10 +1,10 @@
diff --git a/AquaNet/src/components/Mai2Settings.svelte b/AquaNet/src/components/settings/Mai2Settings.svelte
similarity index 93%
rename from AquaNet/src/components/Mai2Settings.svelte
rename to AquaNet/src/components/settings/Mai2Settings.svelte
index 34dfb3f3..544b2ce8 100644
--- a/AquaNet/src/components/Mai2Settings.svelte
+++ b/AquaNet/src/components/settings/Mai2Settings.svelte
@@ -1,10 +1,10 @@
diff --git a/AquaNet/src/libs/generalTypes.ts b/AquaNet/src/libs/generalTypes.ts
index 19274408..4382c0ab 100644
--- a/AquaNet/src/libs/generalTypes.ts
+++ b/AquaNet/src/libs/generalTypes.ts
@@ -133,33 +133,15 @@ export interface GameOption {
changed?: boolean
}
+export interface UserItem { itemKind: number, itemId: number, stock: number }
export interface UserBox {
- userName:string,
- level:number,
- exp:string,
- point:number,
- totalPoint:number,
- playerRating:number,
- highestRating:number,
- nameplateId:number,
- frameId:number,
- characterId:number,
- trophyId:number,
- totalMapNum:number,
- totalHiScore: number,
- totalBasicHighScore:number,
- totalAdvancedHighScore:number,
- totalExpertHighScore:number,
- totalMasterHighScore:number,
- totalUltimaHighScore:number,
- friendCount:number,
- firstPlayDate:Date,
- lastPlayDate:Date,
- courseClass:number,
- overPowerPoint:number,
- overPowerRate:number,
- mapIconId:number,
- voiceId:number,
+ userName: string,
+ nameplateId: number,
+ frameId: number,
+ characterId: number,
+ trophyId: number,
+ mapIconId: number,
+ voiceId: number,
avatarWear: number,
avatarHead: number,
avatarFace: number,
@@ -168,16 +150,3 @@ export interface UserBox {
avatarFront: number,
avatarBack: number,
}
-
-// Assign a number to each kind of user box item with an enum
-export enum UserBoxItemKind {
- nameplate = 1,
- frame = 2,
- trophy = 3,
- mapicon = 8,
- sysvoice = 9,
- avatar = 11,
-}
-
-// Define type only with the keys
-export type UserBoxItemKindStr = keyof typeof UserBoxItemKind
diff --git a/AquaNet/src/libs/i18n/en_ref.ts b/AquaNet/src/libs/i18n/en_ref.ts
index f1d8db52..27e2729b 100644
--- a/AquaNet/src/libs/i18n/en_ref.ts
+++ b/AquaNet/src/libs/i18n/en_ref.ts
@@ -138,8 +138,10 @@ export const EN_REF_SETTINGS = {
'settings.fields.waccaInfiniteWp.desc': 'Set WP to 999999',
'settings.fields.waccaAlwaysVip.name': 'Wacca: Always VIP',
'settings.fields.waccaAlwaysVip.desc': 'Set VIP expiration date to 2077-01-01',
- 'settings.fields.chusanTeamName.name': 'Chunithm Team Name',
+ 'settings.fields.chusanTeamName.name': 'Chuni: Team Name',
'settings.fields.chusanTeamName.desc': 'Customize the text displayed on the top of your profile.',
+ 'settings.fields.chusanInfinitePenguins.name': 'Chuni: Infinite Penguins',
+ 'settings.fields.chusanInfinitePenguins.desc': 'Set penguin statues for character level prompting to 999.',
'settings.fields.rounding.name': 'Score Rounding',
'settings.fields.rounding.desc': 'Round the score to one decimal place',
'settings.fields.optOutOfLeaderboard.name': 'Opt Out of Leaderboard',
@@ -159,26 +161,24 @@ export const EN_REF_SETTINGS = {
}
export const EN_REF_USERBOX = {
- 'userbox.tabs.chusan':'Chuni',
- 'userbox.tabs.maimai':'Mai (WIP)',
- 'userbox.tabs.ongeki':'Ongeki (WIP)',
- 'userbox.nameplate': 'Nameplate',
- 'userbox.frame': 'Frame',
- 'userbox.trophy': 'Trophy (Title)',
- 'userbox.mapicon': 'Map Icon',
- 'userbox.voice':'System Voice',
- 'userbox.wear':'Avatar Wear',
- 'userbox.head':'Avatar Head',
- 'userbox.face':'Avatar Face',
- 'userbox.skin':'Avatar Skin',
- 'userbox.item':'Avatar Item',
- 'userbox.front':'Avatar Front',
- 'userbox.back':'Avatar Back',
- 'userbox.preview.avatar':'Avatar Preview',
- 'userbox.preview.nameplate':'Nameplate Preview',
- 'userbox.preview.ui':'Interface Preview',
- 'userbox.error.noprofile':'No profile was found for this game',
- 'userbox.error.nodata':'No data was found for this game',
+ 'userbox.header.general': 'General Settings',
+ 'userbox.header.userbox': 'UserBox Settings',
+ 'userbox.header.preview': 'UserBox Preview',
+ 'userbox.nameplateId': 'Nameplate',
+ 'userbox.frameId': 'Frame',
+ 'userbox.trophyId': 'Trophy (Title)',
+ 'userbox.mapIconId': 'Map Icon',
+ 'userbox.voiceId': 'System Voice',
+ 'userbox.avatarWear': 'Avatar Wear',
+ 'userbox.avatarHead': 'Avatar Head',
+ 'userbox.avatarFace': 'Avatar Face',
+ 'userbox.avatarSkin': 'Avatar Skin',
+ 'userbox.avatarItem': 'Avatar Item',
+ 'userbox.avatarFront': 'Avatar Front',
+ 'userbox.avatarBack': 'Avatar Back',
+ 'userbox.preview.notice': 'To honor the copyright, we cannot host the images of the userbox items. However, if someone else is willing to provide the images, you can enter their URL here and it will be displayed.',
+ 'userbox.preview.url': 'Image URL',
+ 'userbox.error.nodata': 'Chuni data not found',
}
export const EN_REF = { ...EN_REF_USER, ...EN_REF_Welcome, ...EN_REF_GENERAL,
diff --git a/AquaNet/src/libs/i18n/zh.ts b/AquaNet/src/libs/i18n/zh.ts
index 3c53a2b9..a98e6e93 100644
--- a/AquaNet/src/libs/i18n/zh.ts
+++ b/AquaNet/src/libs/i18n/zh.ts
@@ -4,6 +4,7 @@ import {
EN_REF_LEADERBOARD,
EN_REF_SETTINGS,
EN_REF_USER,
+ EN_REF_USERBOX,
type EN_REF_Welcome
} from "./en_ref";
@@ -147,8 +148,10 @@ const zhSettings: typeof EN_REF_SETTINGS = {
'settings.fields.waccaInfiniteWp.desc': '将 WP 设置为 999999',
'settings.fields.waccaAlwaysVip.name': 'Wacca: 永久会员',
'settings.fields.waccaAlwaysVip.desc': '将 VIP 到期时间设置为 2077-01-01',
- 'settings.fields.chusanTeamName.name': '中二队名',
+ 'settings.fields.chusanTeamName.name': '中二: 队伍名称',
'settings.fields.chusanTeamName.desc': '自定义显示在个人资料顶部的文本。',
+ 'settings.fields.chusanInfinitePenguins.name': '中二: 无限企鹅',
+ 'settings.fields.chusanInfinitePenguins.desc': '将角色界限突破的企鹅雕像数量设置为 999。',
'settings.fields.rounding.name': '分数舍入',
'settings.fields.rounding.desc': '把分数四舍五入到一位小数',
'settings.fields.optOutOfLeaderboard.name': '不参与排行榜',
@@ -167,5 +170,26 @@ const zhSettings: typeof EN_REF_SETTINGS = {
'settings.export': '导出玩家数据',
}
+export const zhUserbox: typeof EN_REF_USERBOX = {
+ 'userbox.header.general': '游戏设置',
+ 'userbox.header.userbox': 'UserBox 设置',
+ 'userbox.header.preview': 'UserBox 预览',
+ 'userbox.nameplateId': '名牌',
+ 'userbox.frameId': '边框',
+ 'userbox.trophyId': '称号',
+ 'userbox.mapIconId': '地图图标',
+ 'userbox.voiceId': '系统语音',
+ 'userbox.avatarWear': '企鹅服饰',
+ 'userbox.avatarHead': '企鹅头饰',
+ 'userbox.avatarFace': '企鹅面部',
+ 'userbox.avatarSkin': '企鹅皮肤',
+ 'userbox.avatarItem': '企鹅物品',
+ 'userbox.avatarFront': '企鹅前景',
+ 'userbox.avatarBack': '企鹅背景',
+ 'userbox.preview.notice': '「生存战略」:为了尊重版权,我们不会提供游戏内物品的图片。但是如果你认识其他愿意提供图床的人,在这里输入 URL 就可以显示出预览。',
+ 'userbox.preview.url': '图床 URL',
+ 'userbox.error.nodata': '未找到中二数据',
+};
+
export const ZH = { ...zhUser, ...zhWelcome, ...zhGeneral,
- ...zhLeaderboard, ...zhHome, ...zhSettings }
+ ...zhLeaderboard, ...zhHome, ...zhSettings, ...zhUserbox }
diff --git a/AquaNet/src/libs/sdk.ts b/AquaNet/src/libs/sdk.ts
index 0e8825db..b6363c5f 100644
--- a/AquaNet/src/libs/sdk.ts
+++ b/AquaNet/src/libs/sdk.ts
@@ -8,7 +8,7 @@ import type {
TrendEntry,
AquaNetUser, GameOption,
UserBox,
- UserBoxItemKind
+ UserItem
} from './generalTypes'
import type { GameName } from './scoring'
@@ -262,13 +262,8 @@ export const USER = {
}
export const USERBOX = {
- getProfile: (): Promise =>
+ getProfile: (): Promise<{ user: UserBox, items: UserItem[] }> =>
get('/api/v2/game/chu3/user-box', {}),
- getUnlockedItems: (itemId: UserBoxItemKind): Promise<{ itemKind: number, itemId: number, stock: number, isValid: boolean }[]> =>
- get(`/api/v2/game/chu3/user-box-item-by-kind`,{ itemId }),
- getItemLabels: () => get(`/api/v2/game/chu3/user-box-all-items`, {}).then(it =>
- Object.fromEntries(Object.entries(it).map(([key, value]) =>
- [key, Object.fromEntries((value as any[]).map(it => [it.id, it.name]))]))),
setUserBox: (d: { field: string, value: number | string }) =>
post(`/api/v2/game/chu3/user-detail-set`, d),
}
@@ -305,7 +300,9 @@ export const GAME = {
export const DATA = {
allMusic: (game: GameName): Promise =>
- fetch(`${DATA_HOST}/d/${game}/00/all-music.json`).then(it => it.json())
+ fetch(`${DATA_HOST}/d/${game}/00/all-music.json`).then(it => it.json()),
+ allItems: (game: GameName): Promise>> =>
+ fetch(`${DATA_HOST}/d/${game}/00/all-items.json`).then(it => it.json()),
}
export const SETTING = {
diff --git a/AquaNet/src/pages/User/Settings.svelte b/AquaNet/src/pages/User/Settings.svelte
index 8f1a0cb9..ca4f8e1b 100644
--- a/AquaNet/src/pages/User/Settings.svelte
+++ b/AquaNet/src/pages/User/Settings.svelte
@@ -9,10 +9,10 @@
import { pfp } from "../../libs/ui";
import { t, ts } from "../../libs/i18n";
import { FADE_IN, FADE_OUT } from "../../libs/config";
- import UserBox from "../../components/UserBox.svelte";
- import Mai2Settings from "../../components/Mai2Settings.svelte";
- import WaccaSettings from "../../components/WaccaSettings.svelte";
- import GeneralGameSettings from "../../components/GeneralGameSettings.svelte";
+ import UserBox from "../../components/settings/ChuniSettings.svelte";
+ import Mai2Settings from "../../components/settings/Mai2Settings.svelte";
+ import WaccaSettings from "../../components/settings/WaccaSettings.svelte";
+ import GeneralGameSettings from "../../components/settings/GeneralGameSettings.svelte";
USER.ensureLoggedIn()
diff --git a/docs/dev/chusan_dev_notes.md b/docs/dev/chusan_dev_notes.md
index 7fdfd978..2e44342e 100644
--- a/docs/dev/chusan_dev_notes.md
+++ b/docs/dev/chusan_dev_notes.md
@@ -1,19 +1,21 @@
# Chusan dev notes
## Item types
-| ItemKind | Name | Single / Multiple | Notes |
-|----------|-------------------|-------------------|-------------------------------|
-| 1 | Nameplate | Single | - |
-| 2 | Frame | Single | - |
-| 3 | Trophy | Single | - |
-| 4 | Skill | Multiple | Stock is level of skill |
-| 5 | Ticket | Multiple | - |
-| 6 | Present | Multiple? | - |
-| 7 | Music (Unlock) | Single | - |
-| 8 | Map Icon | Single | - |
-| 9 | System Voice | Single | - |
-| 10 | Symbol Chat | Single | - |
-| 11 | Avatar Accessory | Single |Part can determined by category|
+| ItemKind | Name | Single / Multiple | Notes |
+|----------|------------------|-------------------|---------------------------------|
+| 1 | Nameplate | Single | - |
+| 2 | Frame | Single | - |
+| 3 | Trophy | Single | - |
+| 4 | Skill | Multiple | Stock is level of skill |
+| 5 | Ticket | Multiple | - |
+| 6 | Present | Multiple? | - |
+| 7 | Music (Unlock) | Single | - |
+| 8 | Map Icon | Single | - |
+| 9 | System Voice | Single | - |
+| 10 | Symbol Chat | Single | - |
+| 11 | Avatar Accessory | Single | Part can determined by category |
+
+Note: Chuni penguin statues are tickets (ok that sounds dumb)
## Avatar accessory category
| Category ID | Part name |
diff --git a/src/main/java/ext/Ext.kt b/src/main/java/ext/Ext.kt
index 8374216b..9671ac8b 100644
--- a/src/main/java/ext/Ext.kt
+++ b/src/main/java/ext/Ext.kt
@@ -21,7 +21,6 @@ import java.security.MessageDigest
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
-import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.concurrent.locks.Lock
diff --git a/src/main/java/icu/samnyan/aqua/net/Bot.kt b/src/main/java/icu/samnyan/aqua/net/Bot.kt
index 256264d4..c7f0bd5b 100644
--- a/src/main/java/icu/samnyan/aqua/net/Bot.kt
+++ b/src/main/java/icu/samnyan/aqua/net/Bot.kt
@@ -3,7 +3,10 @@ package icu.samnyan.aqua.net
import ext.*
import icu.samnyan.aqua.net.db.AquaUserServices
import icu.samnyan.aqua.net.utils.SUCCESS
-import icu.samnyan.aqua.sega.general.service.CardService
+import icu.samnyan.aqua.sega.chusan.model.Chu3Repos
+import icu.samnyan.aqua.sega.general.model.sensitiveInfo
+import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos
+import jakarta.transaction.Transactional
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
@@ -18,12 +21,13 @@ class BotProps {
}
@RestController
-@ConditionalOnProperty("aqua-net.frontier.enabled", havingValue = "true")
+@ConditionalOnProperty("aqua-net.bot.enabled", havingValue = "true")
@API("/api/v2/bot")
class BotController(
- val cardService: CardService,
val us: AquaUserServices,
- val props: BotProps
+ val props: BotProps,
+ val chu3Db: Chu3Repos,
+ val mai2Db: Mai2Repos,
) {
fun Str.checkSecret() {
if (this != props.secret) 403 - "Invalid Secret"
@@ -31,14 +35,55 @@ class BotController(
@PostMapping("/ranking-ban")
@Doc("Ban a user from the leaderboard", "Success status")
- suspend fun rankingBan(@RP secret: Str, @RP username: Str) {
+ suspend fun rankingBan(@RP secret: Str, @RP username: Str): Any {
secret.checkSecret()
- us.cardByName(username) { card ->
+ return us.cardByName(username) { card ->
card.rankingBanned = true
- cardService.cardRepo.save(card)
+ us.cardRepo.save(card)
SUCCESS
}
}
+
+ @Transactional
+ @PostMapping("/debug-user-profile")
+ @Doc("Obtain debug information for a user card", "User card details")
+ fun debugUserProfile(@RP secret: Str, @RP cardId: Str): Any {
+ secret.checkSecret()
+
+ // 1. Check if the card exist
+ var cards = listOfNotNull(
+ us.cardRepo.findByExtId(cardId.long)(),
+ us.cardRepo.findByLuid(cardId)(),
+ us.cardRepo.findById(cardId.long)(),
+ ).toMutableList()
+ cards += cards.flatMap {
+ (it.aquaUser?.cards ?: emptyList()) + listOfNotNull(it.aquaUser?.ghostCard)
+ }
+ cards = cards.distinctBy { it.id }.toMutableList()
+
+ return cards.map { card ->
+ // Find all games played by this card
+ val chu3 = chu3Db.userData.findByCard_ExtId(card.extId)()
+ val mai2 = mai2Db.userData.findByCard_ExtId(card.extId)()
+ val gamesDict = listOfNotNull(chu3, mai2).map {
+ // Find the keychip owner
+ val keychip = it.lastClientId
+ val keychipOwner = keychip?.let { us.userRepo.findByKeychip(it) }
+
+ mapOf(
+ "userData" to it,
+ "keychip" to keychip,
+ "keychipOwner" to keychipOwner,
+ "keychipOwnerCards" to keychipOwner?.cards?.map { it.sensitiveInfo() }
+ )
+ }
+
+ mapOf(
+ "card" to card.sensitiveInfo(),
+ "games" to gamesDict
+ )
+ }
+ }
}
diff --git a/src/main/java/icu/samnyan/aqua/net/db/AquaGameOptions.kt b/src/main/java/icu/samnyan/aqua/net/db/AquaGameOptions.kt
index 15843723..4dae0883 100644
--- a/src/main/java/icu/samnyan/aqua/net/db/AquaGameOptions.kt
+++ b/src/main/java/icu/samnyan/aqua/net/db/AquaGameOptions.kt
@@ -32,8 +32,11 @@ class AquaGameOptions(
@SettingField("wacca")
var waccaAlwaysVip: Boolean = false,
- @SettingField("general")
+ @SettingField("chu3")
var chusanTeamName: String = "",
+
+ @SettingField("chu3")
+ var chusanInfinitePenguins: Boolean = false,
)
interface AquaGameOptionsRepo : JpaRepository
diff --git a/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt b/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt
index 346ad284..a72fc24c 100644
--- a/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt
+++ b/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt
@@ -30,15 +30,13 @@ class Chusan(
"trophyId" to { u, v -> u.trophyId = v.int },
"mapIconId" to { u, v -> u.mapIconId = v.int },
"voiceId" to { u, v -> u.voiceId = v.int },
- "avatarItem" to { u, v -> v.split(':', limit=2).map { it.int }.let { (cat, data) -> when (cat) {
- 1 -> u.avatarWear = data
- 2 -> u.avatarHead = data
- 3 -> u.avatarFace = data
- 4 -> u.avatarSkin = data
- 5 -> u.avatarItem = data
- 6 -> u.avatarFront = data
- 7 -> u.avatarBack = data
- } } }
+ "avatarWear" to { u, v -> u.avatarWear = v.int },
+ "avatarHead" to { u, v -> u.avatarHead = v.int },
+ "avatarFace" to { u, v -> u.avatarFace = v.int },
+ "avatarSkin" to { u, v -> u.avatarSkin = v.int },
+ "avatarItem" to { u, v -> u.avatarItem = v.int },
+ "avatarFront" to { u, v -> u.avatarFront = v.int },
+ "avatarBack" to { u, v -> u.avatarBack = v.int },
) }
override suspend fun userSummary(@RP username: Str, @RP token: String?) = us.cardByName(username) { card ->
@@ -60,11 +58,9 @@ class Chusan(
// UserBox related APIs
@API("user-box")
fun userBox(@RP token: String) = us.jwt.auth(token) {
- userDataRepo.findByCard(it.ghostCard) ?: (404 - "Game data not found") }
-
- @API("user-box-item-by-kind")
- fun userBoxItem(@RP token: String, @RP itemId: Int) = us.jwt.auth(token) {
- rp.userItem.findAllByUser_Card_ExtIdAndItemKind(it.ghostCard.extId, itemId) }
+ val u = userDataRepo.findByCard(it.ghostCard) ?: (404 - "Game data not found")
+ mapOf("user" to u, "items" to rp.userItem.findAllByUser(u))
+ }
@API("user-box-all-items")
fun userBoxAllItems() = allItems
diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt b/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt
index b77eca63..28144a97 100644
--- a/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt
+++ b/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt
@@ -6,6 +6,7 @@ import icu.samnyan.aqua.sega.chusan.model.response.data.MatchingMemberInfo
import icu.samnyan.aqua.sega.chusan.model.response.data.MatchingWaitState
import icu.samnyan.aqua.sega.chusan.model.response.data.UserEmoney
import icu.samnyan.aqua.sega.chusan.model.userdata.UserCharge
+import icu.samnyan.aqua.sega.chusan.model.userdata.UserItem
import icu.samnyan.aqua.sega.chusan.model.userdata.UserMusicDetail
import icu.samnyan.aqua.sega.general.model.response.UserRecentRating
import java.time.format.DateTimeFormatter
@@ -122,13 +123,26 @@ val chusanInit: ChusanController.() -> Unit = {
}
}
+ // Check dev/chusan_dev_notes for more item information
+ val penguins = ls(8000, 8010, 8020, 8030)
+
"GetUserItem".pagedWithKind("userItemList") {
val rawIndex = data["nextIndex"]!!.long
val kind = parsing { (rawIndex / 10000000000L).int }
data["nextIndex"] = rawIndex % 10000000000L
mapOf("itemKind" to kind) grabs {
// TODO: All unlock
- db.userItem.findAllByUser_Card_ExtIdAndItemKind(uid, kind)
+ val items = db.userItem.findAllByUser_Card_ExtIdAndItemKind(uid, kind).toMutableList()
+
+ // Check game options
+ db.userData.findByCard_ExtId(uid)()?.card?.aquaUser?.gameOptions?.let {
+ if (it.chusanInfinitePenguins && kind == 5) {
+ items.removeAll { it.itemId in penguins }
+ items.addAll(penguins.map { UserItem(kind, it, 999, true) })
+ }
+ }
+
+ items
} postProcess {
val ni = it["nextIndex"]!!.long
if (ni != -1L) it["nextIndex"] = ni + (kind * 10000000000L)
diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt b/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt
index f11368b9..0df8ce31 100644
--- a/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt
+++ b/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt
@@ -90,6 +90,7 @@ interface Chu3UserGeneralDataRepo : Chu3UserLinked {
}
interface Chu3UserItemRepo : Chu3UserLinked {
+ fun findAllByUser(user: Chu3UserData): List
fun findTopByUserAndItemIdAndItemKindOrderByIdDesc(user: Chu3UserData, itemId: Int, itemKind: Int): Optional
fun findByUserAndItemIdAndItemKind(user: Chu3UserData, itemId: Int, itemKind: Int): UserItem?
diff --git a/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt b/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt
index bc461438..454b1389 100644
--- a/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt
+++ b/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt
@@ -48,3 +48,5 @@ class Card(
@Suppress("unused") // Used by serialization
val isLinked get() = aquaUser != null
}
+
+fun Card.sensitiveInfo() = mapOf("id" to id, "extId" to extId, "luid" to luid)
diff --git a/src/main/resources/db/migration/mariadb/V1000_23__chusan_infinite_penguins.sql b/src/main/resources/db/migration/mariadb/V1000_23__chusan_infinite_penguins.sql
new file mode 100644
index 00000000..fb281b61
--- /dev/null
+++ b/src/main/resources/db/migration/mariadb/V1000_23__chusan_infinite_penguins.sql
@@ -0,0 +1 @@
+ALTER TABLE aqua_game_options ADD chusan_infinite_penguins BIT(1) NOT NULL DEFAULT 0;