[RF] Re-organize game options

This commit is contained in:
Menci
2025-12-10 23:41:25 +08:00
committed by Azalea
parent 5aca650602
commit f23c0d6fe1
9 changed files with 124 additions and 71 deletions

View File

@@ -1,7 +1,6 @@
<script> <script>
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import { FADE_IN, FADE_OUT } from "../../libs/config"; import { FADE_IN, FADE_OUT } from "../../libs/config";
import GameSettingFields from "./GameSettingFields.svelte";
import { t, ts } from "../../libs/i18n"; import { t, ts } from "../../libs/i18n";
import useLocalStorage from "../../libs/hooks/useLocalStorage.svelte"; import useLocalStorage from "../../libs/hooks/useLocalStorage.svelte";
import RegionSelector from "./RegionSelector.svelte"; import RegionSelector from "./RegionSelector.svelte";
@@ -13,7 +12,6 @@
<blockquote> <blockquote>
{ts("settings.gameNotice")} {ts("settings.gameNotice")}
</blockquote> </blockquote>
<GameSettingFields game="general"/>
<div class="field"> <div class="field">
<div class="bool"> <div class="bool">
<input id="rounding" type="checkbox" bind:checked={rounding.value}/> <input id="rounding" type="checkbox" bind:checked={rounding.value}/>

View File

@@ -149,18 +149,30 @@ export const EN_REF_SETTINGS = {
'settings.tabs.mai2': 'Mai', 'settings.tabs.mai2': 'Mai',
'settings.tabs.ongeki': 'Ongeki', 'settings.tabs.ongeki': 'Ongeki',
'settings.tabs.wacca': 'Wacca', 'settings.tabs.wacca': 'Wacca',
'settings.fields.unlockMusic.name': 'Unlock All Music', 'settings.fields.mai2UnlockMusic.name': 'Unlock All Music',
'settings.fields.unlockMusic.desc': 'Unlock all music and master difficulty in game.', 'settings.fields.mai2UnlockMusic.desc': 'Unlock all music and master difficulty.',
'settings.fields.unlockChara.name': 'Unlock All Characters', 'settings.fields.mai2UnlockChara.name': 'Unlock All Characters',
'settings.fields.unlockChara.desc': 'Unlock all characters, voices, and partners in game.', 'settings.fields.mai2UnlockChara.desc': 'Unlock all characters (new characters start at level 1).',
'settings.fields.unlockCollectables.name': 'Unlock All Collectables', 'settings.fields.mai2UnlockCharaMaxLevel.name': 'Max Character Level',
'settings.fields.unlockCollectables.desc': 'Unlock all collectables (nameplate, title, icon, frame) in game.', 'settings.fields.mai2UnlockCharaMaxLevel.desc': 'Set all characters to max level.',
'settings.fields.unlockTickets.name': 'Unlock All Tickets', 'settings.fields.mai2UnlockPartners.name': 'Unlock All Partners',
'settings.fields.unlockTickets.desc': 'Infinite map/ex tickets (Note: maimai still limits which tickets can be used).', 'settings.fields.mai2UnlockPartners.desc': 'Unlock all partners.',
'settings.fields.waccaInfiniteWp.name': 'Wacca: Infinite WP', 'settings.fields.mai2UnlockCollectables.name': 'Unlock All Collectables',
'settings.fields.waccaInfiniteWp.desc': 'Set WP to 999999', 'settings.fields.mai2UnlockCollectables.desc': 'Unlock all collectables (nameplate, title, icon, frame).',
'settings.fields.waccaAlwaysVip.name': 'Wacca: Always VIP', 'settings.fields.mai2UnlockTickets.name': 'Unlock All Tickets',
'settings.fields.waccaAlwaysVip.desc': 'Set VIP expiration date to 2077-01-01', 'settings.fields.mai2UnlockTickets.desc': 'Infinite tickets (Note: client still limits which tickets can be used).',
'settings.fields.waccaUnlockMusic.name': 'Unlock All Music',
'settings.fields.waccaUnlockMusic.desc': 'Unlock all music.',
'settings.fields.waccaUnlockPlates.name': 'Unlock All Plates',
'settings.fields.waccaUnlockPlates.desc': 'Unlock all plates.',
'settings.fields.waccaUnlockCollectables.name': 'Unlock All Collectables',
'settings.fields.waccaUnlockCollectables.desc': 'Unlock all collectables (icon, trophy).',
'settings.fields.waccaUnlockTickets.name': 'Infinite Tickets',
'settings.fields.waccaUnlockTickets.desc': 'Infinite tickets.',
'settings.fields.waccaInfiniteWp.name': 'Infinite WP',
'settings.fields.waccaInfiniteWp.desc': 'Set WP to 999999.',
'settings.fields.waccaAlwaysVip.name': 'Always VIP',
'settings.fields.waccaAlwaysVip.desc': 'Set VIP expiration date to 2077-01-01.',
'settings.fields.chusanTeamName.name': 'Team Name', 'settings.fields.chusanTeamName.name': 'Team Name',
'settings.fields.chusanTeamName.desc': 'Customize the text displayed on the top of your profile.', 'settings.fields.chusanTeamName.desc': 'Customize the text displayed on the top of your profile.',
'settings.fields.chusanInfinitePenguins.name': 'Infinite Penguins', 'settings.fields.chusanInfinitePenguins.name': 'Infinite Penguins',

View File

@@ -161,18 +161,30 @@ const zhSettings: typeof EN_REF_SETTINGS = {
'settings.tabs.mai2': '舞萌', 'settings.tabs.mai2': '舞萌',
'settings.tabs.ongeki': '音击', 'settings.tabs.ongeki': '音击',
'settings.tabs.wacca': '华卡', 'settings.tabs.wacca': '华卡',
'settings.fields.unlockMusic.name': '解锁谱面', 'settings.fields.mai2UnlockMusic.name': '解锁谱面',
'settings.fields.unlockMusic.desc': '在游戏中解锁所有曲目和大师难度谱面。', 'settings.fields.mai2UnlockMusic.desc': '解锁所有曲目和大师难度谱面。',
'settings.fields.unlockChara.name': '解锁角色', 'settings.fields.mai2UnlockChara.name': '解锁角色',
'settings.fields.unlockChara.desc': '在游戏中解锁所有角色、语音和伙伴。', 'settings.fields.mai2UnlockChara.desc': '解锁所有角色(新角色从 1 级开始)。',
'settings.fields.unlockCollectables.name': '解锁收藏品', 'settings.fields.mai2UnlockCharaMaxLevel.name': '角色满级',
'settings.fields.unlockCollectables.desc': '在游戏中解锁所有收藏品(名牌、称号、图标、背景图)。', 'settings.fields.mai2UnlockCharaMaxLevel.desc': '将所有角色设置为满级。',
'settings.fields.unlockTickets.name': '解锁游戏券', 'settings.fields.mai2UnlockPartners.name': '解锁搭档',
'settings.fields.unlockTickets.desc': '无限跑图券/解锁券maimai 客户端仍限制一些券不能使用)。', 'settings.fields.mai2UnlockPartners.desc': '解锁所有搭档。',
'settings.fields.waccaInfiniteWp.name': '华卡:无限 WP', 'settings.fields.mai2UnlockCollectables.name': '解锁收藏品',
'settings.fields.waccaInfiniteWp.desc': '将 WP 设置为 999999', 'settings.fields.mai2UnlockCollectables.desc': '解锁所有收藏品(姓名框、称号、头像、背景)。',
'settings.fields.waccaAlwaysVip.name': '华卡:永久会员', 'settings.fields.mai2UnlockTickets.name': '解锁功能票',
'settings.fields.waccaAlwaysVip.desc': '将 VIP 到期时间设置为 2077-01-01', 'settings.fields.mai2UnlockTickets.desc': '无限功能票(注:客户端仍限制一些功能票不能使用)。',
'settings.fields.waccaUnlockMusic.name': '解锁谱面',
'settings.fields.waccaUnlockMusic.desc': '解锁所有曲目。',
'settings.fields.waccaUnlockPlates.name': '解锁铭牌',
'settings.fields.waccaUnlockPlates.desc': '解锁所有铭牌。',
'settings.fields.waccaUnlockCollectables.name': '解锁收藏品',
'settings.fields.waccaUnlockCollectables.desc': '解锁所有收藏品。',
'settings.fields.waccaUnlockTickets.name': '无限解锁券',
'settings.fields.waccaUnlockTickets.desc': '无限解锁券。',
'settings.fields.waccaInfiniteWp.name': '无限 WP',
'settings.fields.waccaInfiniteWp.desc': '将 WP 设置为 999999。',
'settings.fields.waccaAlwaysVip.name': '永久会员',
'settings.fields.waccaAlwaysVip.desc': '将 VIP 到期时间设置为 2077-01-01。',
'settings.fields.chusanTeamName.name': '队伍名称', 'settings.fields.chusanTeamName.name': '队伍名称',
'settings.fields.chusanTeamName.desc': '自定义显示在个人资料顶部的文本。', 'settings.fields.chusanTeamName.desc': '自定义显示在个人资料顶部的文本。',
'settings.fields.chusanInfinitePenguins.name': '我是桐谷遥', 'settings.fields.chusanInfinitePenguins.name': '我是桐谷遥',

View File

@@ -35,8 +35,10 @@ import java.util.concurrent.locks.Lock
import kotlin.reflect.KCallable import kotlin.reflect.KCallable
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.jvmErasure import kotlin.reflect.jvm.jvmErasure
typealias RP = RequestParam typealias RP = RequestParam
@@ -81,7 +83,7 @@ annotation class SettingField(
// Reflection // Reflection
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T : Any> KClass<T>.vars() = memberProperties.mapNotNull { it as? Var<T, Any> } fun <T : Any> KClass<T>.vars() = declaredMemberProperties.sortedBy { it.javaField?.declaringClass?.declaredFields?.indexOf(it.javaField) ?: Int.MAX_VALUE }.mapNotNull { it as? Var<T, Any> }
fun <T : Any> KClass<T>.varsMap() = vars().associateBy { it.name } fun <T : Any> KClass<T>.varsMap() = vars().associateBy { it.name }
fun <T : Any> KClass<T>.getters() = java.methods.filter { it.name.startsWith("get") } fun <T : Any> KClass<T>.getters() = java.methods.filter { it.name.startsWith("get") }
fun <T : Any> KClass<T>.gettersMap() = getters().associateBy { it.name.removePrefix("get").firstCharLower() } fun <T : Any> KClass<T>.gettersMap() = getters().associateBy { it.name.removePrefix("get").firstCharLower() }

View File

@@ -2,10 +2,7 @@ package icu.samnyan.aqua.net.db
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import ext.SettingField import ext.SettingField
import jakarta.persistence.Entity import jakarta.persistence.*
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
@Entity @Entity
@@ -14,21 +11,29 @@ class AquaGameOptions(
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0, var id: Long = 0,
@SettingField("general") @SettingField("mai2") @Column(name = "mai2_unlock_music")
var unlockMusic: Boolean = false, var mai2UnlockMusic: Boolean = false,
@SettingField("mai2") @Column(name = "mai2_unlock_chara")
@SettingField("general") var mai2UnlockChara: Boolean = false,
var unlockChara: Boolean = false, @SettingField("mai2") @Column(name = "mai2_unlock_chara_max_level")
var mai2UnlockCharaMaxLevel: Boolean = false,
@SettingField("general") @SettingField("mai2") @Column(name = "mai2_unlock_partners")
var unlockCollectables: Boolean = false, var mai2UnlockPartners: Boolean = false,
@SettingField("mai2") @Column(name = "mai2_unlock_collectables")
@SettingField("general") var mai2UnlockCollectables: Boolean = false,
var unlockTickets: Boolean = false, @SettingField("mai2") @Column(name = "mai2_unlock_tickets")
var mai2UnlockTickets: Boolean = false,
@SettingField("wacca")
var waccaUnlockMusic: Boolean = false,
@SettingField("wacca")
var waccaUnlockPlates: Boolean = false,
@SettingField("wacca")
var waccaUnlockCollectables: Boolean = false,
@SettingField("wacca")
var waccaUnlockTickets: Boolean = false,
@SettingField("wacca") @SettingField("wacca")
var waccaInfiniteWp: Boolean = false, var waccaInfiniteWp: Boolean = false,
@SettingField("wacca") @SettingField("wacca")
var waccaAlwaysVip: Boolean = false, var waccaAlwaysVip: Boolean = false,

View File

@@ -1,11 +1,13 @@
package icu.samnyan.aqua.sega.maimai2.handler package icu.samnyan.aqua.sega.maimai2.handler
import ext.int
import ext.logger import ext.logger
import icu.samnyan.aqua.net.games.mai2.Maimai2 import icu.samnyan.aqua.net.games.mai2.Maimai2
import icu.samnyan.aqua.sega.general.BaseHandler import icu.samnyan.aqua.sega.general.BaseHandler
import icu.samnyan.aqua.sega.general.dao.CardRepository import icu.samnyan.aqua.sega.general.dao.CardRepository
import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2ItemKind import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2ItemKind
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserCharacter
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
import kotlin.jvm.optionals.getOrNull import kotlin.jvm.optionals.getOrNull
@@ -15,34 +17,25 @@ class GetUserCharacterHandler(
val maimai2: Maimai2, val maimai2: Maimai2,
val cardRepo: CardRepository, val cardRepo: CardRepository,
) : BaseHandler { ) : BaseHandler {
val itemUnlock = maimai2.itemMapping[Mai2ItemKind.chara.name]?.map { mapOf( val charaIds = maimai2.itemMapping[Mai2ItemKind.chara.name]?.map { it.key.int() } ?: emptyList()
"characterId" to it.key,
"level" to 9999,
"awakening" to 1,
"useCount" to 0
) }
init { init {
if (itemUnlock.isNullOrEmpty()) logger.warn("Mai2 item info is empty") if (charaIds.isEmpty()) logger.warn("Mai2 item info is empty")
} }
override fun handle(request: Map<String, Any>): Any { override fun handle(request: Map<String, Any>): Any {
val userId = (request["userId"] as Number).toLong() val userId = (request["userId"] as Number).toLong()
val gameOptions = cardRepo.findByExtId(userId).getOrNull()?.aquaUser?.gameOptions
// Aqua Net game unlock feature val userCharacterList = repos.userCharacter.findByUser_Card_ExtId(userId)
cardRepo.findByExtId(userId).getOrNull()?.aquaUser?.gameOptions?.let { opt -> .let { if (gameOptions?.mai2UnlockChara != true) it else (
if (!opt.unlockChara or itemUnlock.isNullOrEmpty()) return@let charaIds.associateWith { Mai2UserCharacter().apply { characterId = it; level = 1 } } +
it.associateBy { it.characterId }
logger.info("Response: ${itemUnlock!!.size} Characters - All unlock") ).values }
return mapOf( .let { if (gameOptions?.mai2UnlockCharaMaxLevel != true) it else it.map { it.apply { level = 9999 } } }
"userId" to userId,
"userCharacterList" to itemUnlock
)
}
return mapOf( return mapOf(
"userId" to userId, "userId" to userId,
"userCharacterList" to repos.userCharacter.findByUser_Card_ExtId(userId) "userCharacterList" to userCharacterList
) )
} }

View File

@@ -50,10 +50,11 @@ class GetUserItemHandler(
// Aqua Net game unlock feature // Aqua Net game unlock feature
cardRepo.findByExtId(userId).getOrNull()?.aquaUser?.gameOptions?.let { opt -> cardRepo.findByExtId(userId).getOrNull()?.aquaUser?.gameOptions?.let { opt ->
val items = when { val items = when {
(kind in 5..8) && opt.unlockMusic -> musicUnlock.getValue(kind) (kind in 5..8) && opt.mai2UnlockMusic -> musicUnlock.getValue(kind)
(kind in 1..3 || kind == 11) && opt.unlockCollectables -> itemUnlock[kind] (kind in 1..3 || kind == 11) && opt.mai2UnlockCollectables -> itemUnlock[kind]
(kind == 12) && opt.unlockTickets -> itemUnlock[kind] (kind == 12) && opt.mai2UnlockTickets -> itemUnlock[kind]
(kind in 9..10) && opt.unlockChara -> itemUnlock[kind] (kind == 9) && opt.mai2UnlockChara -> itemUnlock[kind]
(kind == 10) && opt.mai2UnlockPartners -> itemUnlock[kind]
else -> emptyList() else -> emptyList()
} }

View File

@@ -205,19 +205,19 @@ fun WaccaServer.init() {
val go = u.card?.aquaUser?.gameOptions ?: AquaGameOptions() val go = u.card?.aquaUser?.gameOptions ?: AquaGameOptions()
// All unlock // All unlock
if (go.unlockMusic && wacca.musicMapping.isNotEmpty()) { if (go.waccaUnlockMusic && wacca.musicMapping.isNotEmpty()) {
items[MUSIC_UNLOCK()] = wacca.musicMapping.map { (id, v) -> MUSIC_UNLOCK(u, id, p1 = v.notes.size.long() - 1) } items[MUSIC_UNLOCK()] = wacca.musicMapping.map { (id, v) -> MUSIC_UNLOCK(u, id, p1 = v.notes.size.long() - 1) }
} }
if (go.unlockTickets) { if (go.waccaUnlockTickets) {
var i = 0 var i = 0
items[TICKET()] = enabledTickets.flatMap { (1..5).map { TICKET(u, it).apply { id = (i++).toLong() } } } items[TICKET()] = enabledTickets.flatMap { (1..5).map { TICKET(u, it).apply { id = (i++).toLong() } } }
} }
if (go.unlockChara) { if (go.waccaUnlockPlates) {
wacca.itemMapping["plates"]?.let { items[USER_PLATE()] = it.map { (k, _) -> USER_PLATE(u, k.int()) } } wacca.itemMapping["plates"]?.let { items[USER_PLATE()] = it.map { (k, _) -> USER_PLATE(u, k.int()) } }
} }
if (go.unlockCollectables) { if (go.waccaUnlockCollectables) {
// TODO: Add titles // TODO: Add titles
mapOf("icon" to ICON, "plates" to USER_PLATE, "trophy" to TROPHY).map { (name, type) -> mapOf("icon" to ICON, "trophy" to TROPHY).map { (name, type) ->
wacca.itemMapping[name]?.let { items[type()] = it.map { (k, _) -> type(u, k.int()) } } wacca.itemMapping[name]?.let { items[type()] = it.map { (k, _) -> type(u, k.int()) } }
} }
} }

View File

@@ -0,0 +1,30 @@
-- Add new unlock columns
ALTER TABLE aqua_game_options ADD COLUMN mai2_unlock_music BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN mai2_unlock_chara BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN mai2_unlock_chara_max_level BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN mai2_unlock_partners BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN mai2_unlock_collectables BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN mai2_unlock_tickets BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN wacca_unlock_music BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN wacca_unlock_plates BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN wacca_unlock_collectables BIT NOT NULL DEFAULT 0;
ALTER TABLE aqua_game_options ADD COLUMN wacca_unlock_tickets BIT NOT NULL DEFAULT 0;
-- Migrate data
UPDATE aqua_game_options SET
mai2_unlock_music = unlock_music,
mai2_unlock_chara = unlock_chara,
mai2_unlock_chara_max_level = unlock_chara,
mai2_unlock_partners = unlock_chara,
mai2_unlock_collectables = unlock_collectables,
mai2_unlock_tickets = unlock_tickets,
wacca_unlock_music = unlock_music,
wacca_unlock_plates = unlock_chara | unlock_collectables,
wacca_unlock_collectables = unlock_collectables,
wacca_unlock_tickets = unlock_tickets;
-- Drop old columns
ALTER TABLE aqua_game_options DROP COLUMN unlock_music;
ALTER TABLE aqua_game_options DROP COLUMN unlock_chara;
ALTER TABLE aqua_game_options DROP COLUMN unlock_collectables;
ALTER TABLE aqua_game_options DROP COLUMN unlock_tickets;