From 2a88f380e103673355f7fee38df1bad9311804ac Mon Sep 17 00:00:00 2001 From: Green00101 <2585640230@qq.com> Date: Mon, 27 Apr 2026 17:18:57 +1000 Subject: [PATCH 1/2] Add configurable creature base stat max value --- assets/i18n/de.json | 7 ++- assets/i18n/en.json | 7 ++- assets/i18n/es.json | 7 ++- assets/i18n/fr.json | 5 +- assets/i18n/it.json | 7 ++- assets/i18n/ja.json | 7 ++- assets/i18n/nl.json | 7 ++- assets/i18n/pt.json | 7 ++- assets/i18n/zh_Hans.json | 7 ++- assets/i18n/zh_Hant.json | 7 ++- src/i18n.ts | 2 +- .../addBaseStatMaxValueToSettings.ts | 21 +++++++ src/migrations/migrationConfig.ts | 48 +++++++++------- src/models/entities/config.ts | 5 +- src/models/entities/creature.ts | 14 ++--- src/utils/cleanNaNValue.ts | 5 +- .../dashboard/DashboardSettings.tsx | 36 ++++++++++-- .../database/pokemon/editors/StatsEditor.tsx | 55 ++++++++++++++----- 18 files changed, 185 insertions(+), 69 deletions(-) create mode 100644 src/migrations/addBaseStatMaxValueToSettings.ts diff --git a/assets/i18n/de.json b/assets/i18n/de.json index 592970069..626c329e8 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/en.json b/assets/i18n/en.json index c1dad8a1c..cdfe030bd 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/es.json b/assets/i18n/es.json index f728a65df..235829e2e 100644 --- a/assets/i18n/es.json +++ b/assets/i18n/es.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index 183c25124..761d0ee47 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -1864,5 +1864,8 @@ "create_event": "Ajouter un évènement", "invalid_data": "Données invalides", "event_title_empty_state": "Commencez à créer un événement !", - "event_description_empty_state": "Vous n'avez pas encore d'événement. Vous pouvez créer un nouvel événement ou importer des événements existants." + "event_description_empty_state": "Vous n'avez pas encore d'événement. Vous pouvez créer un nouvel événement ou importer des événements existants.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" } diff --git a/assets/i18n/it.json b/assets/i18n/it.json index 510cedd20..a0decc072 100644 --- a/assets/i18n/it.json +++ b/assets/i18n/it.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/ja.json b/assets/i18n/ja.json index f31657cb2..1451d6d9c 100644 --- a/assets/i18n/ja.json +++ b/assets/i18n/ja.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/nl.json b/assets/i18n/nl.json index c2120647d..033784f01 100644 --- a/assets/i18n/nl.json +++ b/assets/i18n/nl.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/pt.json b/assets/i18n/pt.json index fcadfb575..05e032b13 100644 --- a/assets/i18n/pt.json +++ b/assets/i18n/pt.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/zh_Hans.json b/assets/i18n/zh_Hans.json index 311af5f7b..429f9c96f 100644 --- a/assets/i18n/zh_Hans.json +++ b/assets/i18n/zh_Hans.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/assets/i18n/zh_Hant.json b/assets/i18n/zh_Hant.json index 63ad94036..a86a1c40f 100644 --- a/assets/i18n/zh_Hant.json +++ b/assets/i18n/zh_Hant.json @@ -1864,5 +1864,8 @@ "create_event": "Add event", "invalid_data": "Invalid data", "event_title_empty_state": "Start creating an event!", - "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones." -} \ No newline at end of file + "event_description_empty_state": "You don't have any event yet. You can create a new event or import existing ones.", + "max_base_stat_value": "Max base stat value", + "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", + "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" +} diff --git a/src/i18n.ts b/src/i18n.ts index 044037f93..499f6dd3e 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,6 +1,6 @@ import i18next, { InitOptions } from 'i18next'; -import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; +import { initReactI18next } from 'react-i18next'; import { languages } from '@root/package.json'; diff --git a/src/migrations/addBaseStatMaxValueToSettings.ts b/src/migrations/addBaseStatMaxValueToSettings.ts new file mode 100644 index 000000000..ae6b02c00 --- /dev/null +++ b/src/migrations/addBaseStatMaxValueToSettings.ts @@ -0,0 +1,21 @@ +import { SETTINGS_CONFIG_VALIDATOR, StudioSettingConfig } from '@modelEntities/config'; +import { parseJSON } from '@utils/json/parse'; +import { IpcMainEvent } from 'electron'; +import fsPromise from 'fs/promises'; +import path from 'path'; +import { deletePSDKDatFile } from './migrateUtils'; + +const PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR = SETTINGS_CONFIG_VALIDATOR.omit({ baseStatMaxValue: true }); + +export const addBaseStatMaxValueToSettings = async (_: IpcMainEvent, projectPath: string) => { + deletePSDKDatFile(projectPath); + const settingsFilePath = path.join(projectPath, 'Data/configs/settings_config.json'); + const settingsFile = await fsPromise.readFile(settingsFilePath, { encoding: 'utf-8' }); + const settingsFileParsed = PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR.safeParse(parseJSON(settingsFile, 'settings_config.json')); + if (!settingsFileParsed.success) throw new Error('Fail to parse settings_config.json file'); + const newSettingsFile: StudioSettingConfig = { + ...settingsFileParsed.data, + baseStatMaxValue: 999, + }; + await fsPromise.writeFile(settingsFilePath, JSON.stringify(newSettingsFile, null, 2)); +}; diff --git a/src/migrations/migrationConfig.ts b/src/migrations/migrationConfig.ts index 223df504d..cd39c9881 100644 --- a/src/migrations/migrationConfig.ts +++ b/src/migrations/migrationConfig.ts @@ -1,31 +1,32 @@ import type { MigrationTask } from '@src/backendTasks/migrateData'; -import { migrateMapLinks } from './migrateMapLinks'; -import { linkResourcesToCreatures } from './linkResourcesToCreatures'; -import { migrateHeadbutt } from './migrateHeadbutt'; -import { fixBeMethodMoveSelfStatus } from './fixBeMethodMoveSelfStatus'; -import { migrationV2 } from './migrationV2'; import { addAvailableLanguagesForTranslation } from './addAvailableLanguagesForTranslation'; -import { addVolumeAndPitchInMaps } from './addVolumeAndPitchInMaps'; -import { generatingMapOverviews } from './generatingMapOverviews'; -import { addOtherLanguages } from './addOtherLanguages'; -import { fixCreatureValuesAfterZodChange } from './fixCreatureValuesAfterZodChange'; -import { addFormNamesDescriptions } from './addFormNamesDescriptions'; -import { migrateNaturesToEntities } from './migrateNaturesToEntities'; -import { migrateUndefinedBreedingGroupToUnknown } from './migrateUndefinedBreedingGroupToUnknown'; -import { addTrainerAdditionalDialogs } from './addTrainerAdditionalDialogs'; -import { migrateQuestsEarnings } from './migrateQuestsEarnings'; +import { addBaseStatMaxValueToSettings } from './addBaseStatMaxValueToSettings'; +import { addBattleCamera3dToSettings } from './addBattleCamera3dToSettings'; +import { addBerryAndCookingDataToItems } from './addBerryAndCookingDataToItems'; +import { addCsvForEventFolders } from './addCsvForEventFolders'; import { addCsvForQuestsCustomObjectives } from './addCsvForQuestsCustomObjectives'; import { addEggInCreatureResources } from './addEggInCreatureResources'; -import { addBattleCamera3dToSettings } from './addBattleCamera3dToSettings'; -import { migrateGroupsFor3v3BattleMode } from './migrateGroupsFor3v3BattleMode'; +import { addFormNamesDescriptions } from './addFormNamesDescriptions'; import { addMegaEvolutionParameterToItems } from './addMegaEvolutionParameterToItems'; -import { addBerryAndCookingDataToItems } from './addBerryAndCookingDataToItems'; -import { addSoundDesignConfig } from './addSoundDesignConfig'; -import { addMoveContestData } from './addMoveContestData'; import { addMissingMapLinks } from './addMissingMapLinks'; -import { baseForEvents } from './baseForEvents'; -import { addCsvForEventFolders } from './addCsvForEventFolders'; +import { addMoveContestData } from './addMoveContestData'; +import { addOtherLanguages } from './addOtherLanguages'; +import { addSoundDesignConfig } from './addSoundDesignConfig'; import { addSummaryPagesOptionsToSettings } from './addSummaryPagesOptionsToSettings'; +import { addTrainerAdditionalDialogs } from './addTrainerAdditionalDialogs'; +import { addVolumeAndPitchInMaps } from './addVolumeAndPitchInMaps'; +import { baseForEvents } from './baseForEvents'; +import { fixBeMethodMoveSelfStatus } from './fixBeMethodMoveSelfStatus'; +import { fixCreatureValuesAfterZodChange } from './fixCreatureValuesAfterZodChange'; +import { generatingMapOverviews } from './generatingMapOverviews'; +import { linkResourcesToCreatures } from './linkResourcesToCreatures'; +import { migrateGroupsFor3v3BattleMode } from './migrateGroupsFor3v3BattleMode'; +import { migrateHeadbutt } from './migrateHeadbutt'; +import { migrateMapLinks } from './migrateMapLinks'; +import { migrateNaturesToEntities } from './migrateNaturesToEntities'; +import { migrateQuestsEarnings } from './migrateQuestsEarnings'; +import { migrateUndefinedBreedingGroupToUnknown } from './migrateUndefinedBreedingGroupToUnknown'; +import { migrationV2 } from './migrationV2'; type MigrateConfigType = { migration: MigrationTask; @@ -185,4 +186,9 @@ export const MIGRATION_CONFIG: MigrateConfigType[] = [ version: '2.9.1', message: 'add_summary_pages_options_to_settings', }, + { + migration: addBaseStatMaxValueToSettings, + version: '2.9.2', + message: 'add_base_stat_max_value_to_settings', + }, ]; diff --git a/src/models/entities/config.ts b/src/models/entities/config.ts index c5e968195..fdf585a75 100644 --- a/src/models/entities/config.ts +++ b/src/models/entities/config.ts @@ -15,7 +15,7 @@ export const CREDIT_CONFIG_VALIDATOR = z.object({ z.object({ title: z.string(), name: z.string(), - }) + }), ), gameCredits: z.string(), }); @@ -105,6 +105,7 @@ export const SETTINGS_CONFIG_VALIDATOR = z.object({ isUseBattleCamera3d: z.boolean(), showContestSummaryPage: z.boolean(), showRibbonsSummaryPage: z.boolean(), + baseStatMaxValue: POSITIVE_INT.max(9999).default(999), }); export type StudioSettingConfig = z.infer; @@ -241,4 +242,4 @@ export type SoundEffectsKeys = | keyof SoundDesignConfig['backgroundMusic'] | keyof SoundDesignConfig['backgroundSound']; -export type SoundLocated = keyof SoundDesignConfig; \ No newline at end of file +export type SoundLocated = keyof SoundDesignConfig; diff --git a/src/models/entities/creature.ts b/src/models/entities/creature.ts index cc2d05aa8..97d10f81f 100644 --- a/src/models/entities/creature.ts +++ b/src/models/entities/creature.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { POSITIVE_OR_ZERO_INT, POSITIVE_INT, POSITIVE_OR_ZERO_FLOAT } from './common'; +import { POSITIVE_INT, POSITIVE_OR_ZERO_FLOAT, POSITIVE_OR_ZERO_INT } from './common'; import { DB_SYMBOL_VALIDATOR } from './dbSymbol'; export const ITEM_HELD_VALIDATOR = z.object({ @@ -111,12 +111,12 @@ export const CREATURE_FORM_VALIDATOR = z.object({ weight: POSITIVE_OR_ZERO_FLOAT.min(0.01).max(9999.99).step(0.01), type1: DB_SYMBOL_VALIDATOR, type2: DB_SYMBOL_VALIDATOR, - baseHp: POSITIVE_INT.max(255), - baseAtk: POSITIVE_INT.max(255), - baseDfe: POSITIVE_INT.max(255), - baseSpd: POSITIVE_INT.max(255), - baseAts: POSITIVE_INT.max(255), - baseDfs: POSITIVE_INT.max(255), + baseHp: POSITIVE_INT.max(9999), + baseAtk: POSITIVE_INT.max(9999), + baseDfe: POSITIVE_INT.max(9999), + baseSpd: POSITIVE_INT.max(9999), + baseAts: POSITIVE_INT.max(9999), + baseDfs: POSITIVE_INT.max(9999), evHp: POSITIVE_OR_ZERO_INT.max(255), evAtk: POSITIVE_OR_ZERO_INT.max(255), evDfe: POSITIVE_OR_ZERO_INT.max(255), diff --git a/src/utils/cleanNaNValue.ts b/src/utils/cleanNaNValue.ts index 252c33fd3..88b6a6135 100644 --- a/src/utils/cleanNaNValue.ts +++ b/src/utils/cleanNaNValue.ts @@ -1,13 +1,13 @@ +import { PokemonBattlerFrom } from '@components/pokemonBattler/editors/PokemonBattlerEditorOverlay'; import { StudioDisplayConfig, StudioSaveConfig, StudioSettingConfig, StudioTextConfig } from '@modelEntities/config'; import { StudioCreatureForm } from '@modelEntities/creature'; +import { StudioGroup } from '@modelEntities/group'; import { StudioExpandPokemonSetup, StudioGroupEncounter, StudioIvEv } from '@modelEntities/groupEncounter'; import { StudioItem } from '@modelEntities/item'; import { StudioMove } from '@modelEntities/move'; import { StudioTrainer } from '@modelEntities/trainer'; import { StudioZone } from '@modelEntities/zone'; -import { StudioGroup } from '@modelEntities/group'; import { ProjectData, State } from '@src/GlobalStateProvider'; -import { PokemonBattlerFrom } from '@components/pokemonBattler/editors/PokemonBattlerEditorOverlay'; import { assertUnreachable } from './assertUnreachable'; /** @@ -43,6 +43,7 @@ export const cleaningSaveNaNValues = (v: StudioSaveConfig) => { export const cleaningSettingsNaNValues = (v: StudioSettingConfig) => { v.pokemonMaxLevel = cleanNaNValue(v.pokemonMaxLevel, 100); v.maxBagItemCount = cleanNaNValue(v.maxBagItemCount); + v.baseStatMaxValue = cleanNaNValue(v.baseStatMaxValue, 999); }; export const cleaningTextNaNValues = (v: StudioTextConfig) => { diff --git a/src/views/components/dashboard/DashboardSettings.tsx b/src/views/components/dashboard/DashboardSettings.tsx index 552b6f456..2798d7928 100644 --- a/src/views/components/dashboard/DashboardSettings.tsx +++ b/src/views/components/dashboard/DashboardSettings.tsx @@ -1,11 +1,11 @@ -import React, { useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { InputWithLeftLabelContainer, Input, Label, Toggle, InputContainer } from '@components/inputs'; +import { Input, InputContainer, InputWithLeftLabelContainer, Label, Toggle } from '@components/inputs'; import { PageEditor } from '@components/pages'; import { useConfigSettings } from '@hooks/useProjectConfig'; -import styled from 'styled-components'; import { cleaningSettingsNaNValues } from '@utils/cleanNaNValue'; import { cloneEntity } from '@utils/cloneEntity'; +import React, { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; const UnlimitedItemsInfoContainer = styled.span` ${({ theme }) => theme.fonts.normalSmall} @@ -17,12 +17,14 @@ export const DashboardSettings = () => { const { projectConfigValues: settings, setProjectConfigValues: setSettings } = useConfigSettings(); const [maxLevel, setMaxLevel] = useState(settings.pokemonMaxLevel); const [maxItemCount, setMaxBagItemCount] = useState(settings.maxBagItemCount); + const [baseStatMaxValue, setBaseStatMaxValue] = useState(settings.baseStatMaxValue); const currentEditedSettings = useMemo(() => cloneEntity(settings), [settings]); const updateSettingsConfig = () => { cleaningSettingsNaNValues(currentEditedSettings); setMaxLevel(currentEditedSettings.pokemonMaxLevel); setMaxBagItemCount(currentEditedSettings.maxBagItemCount); + setBaseStatMaxValue(currentEditedSettings.baseStatMaxValue); setSettings(currentEditedSettings); }; @@ -39,6 +41,19 @@ export const DashboardSettings = () => { updateSettingsConfig(); }; + const onChangeBaseStatMaxValue = (event: React.ChangeEvent) => { + const baseStatMax = parseInt(event.target.value); + if (baseStatMax < 1 || baseStatMax > 9999) return event.preventDefault(); + setBaseStatMaxValue(baseStatMax); + }; + + const onBlurBaseStatMaxValue = (event: React.ChangeEvent) => { + const baseStatMax = parseInt(event.target.value); + if (baseStatMax < 1 || baseStatMax > 9999) return event.preventDefault(); + currentEditedSettings.baseStatMaxValue = baseStatMax; + updateSettingsConfig(); + }; + const onChangeMaxBagItemCount = (event: React.ChangeEvent) => { const maxBagItem = parseInt(event.target.value); if (maxBagItem < 0 || maxBagItem > 9999) return event.preventDefault(); @@ -68,6 +83,19 @@ export const DashboardSettings = () => { placeholder="100" /> + + + + theme.fonts.normalRegular} + background-color: ${({ theme }) => theme.colors.warningSoft}; + border: 1px solid ${({ theme }) => theme.colors.warningSoft}; + border-radius: 8px; + color: ${({ theme }) => theme.colors.warningBase}; + box-sizing: border-box; +`; + const getStat = (stat: string, defaults: Record, formData: Record) => { - return Number(formData[stat] ? formData[stat] : defaults[stat] ?? 0); + return Number(formData[stat] ? formData[stat] : (defaults[stat] ?? 0)); }; const calculateTotal = (defaults: Record, getRawFormData: () => Record) => { const formData = getRawFormData(); return ['baseHp', 'baseAtk', 'baseDfe', 'baseAts', 'baseDfs', 'baseSpd'].reduce((prev, stat) => prev + getStat(stat, defaults, formData), 0); }; -const STATS_EDITOR_SCHEMA = CREATURE_FORM_VALIDATOR.pick({ - ...{ baseHp: true, baseAtk: true, baseDfe: true, baseAts: true, baseDfs: true, baseSpd: true }, - ...{ evHp: true, evAtk: true, evDfe: true, evAts: true, evDfs: true, evSpd: true }, -}); - export const StatEditor = forwardRef((_, ref) => { const { t } = useTranslation(); const { creature, form } = useCreaturePage(); const updateForm = useUpdateForm(creature, form); const totalRef = useRef(null); + const isAboveRecommended = (data: Record) => + ['baseHp', 'baseAtk', 'baseDfe', 'baseAts', 'baseDfs', 'baseSpd'].some((k) => Number(data[k] ?? 0) > 255); + const { projectConfigValues: settings } = useConfigSettings(); + const STATS_EDITOR_SCHEMA = useMemo( + () => + CREATURE_FORM_VALIDATOR.pick({ + ...{ baseHp: true, baseAtk: true, baseDfe: true, baseAts: true, baseDfs: true, baseSpd: true }, + ...{ evHp: true, evAtk: true, evDfe: true, evAts: true, evDfs: true, evSpd: true }, + }).extend({ + baseHp: POSITIVE_INT.max(settings.baseStatMaxValue), + baseAtk: POSITIVE_INT.max(settings.baseStatMaxValue), + baseDfe: POSITIVE_INT.max(settings.baseStatMaxValue), + baseAts: POSITIVE_INT.max(settings.baseStatMaxValue), + baseDfs: POSITIVE_INT.max(settings.baseStatMaxValue), + baseSpd: POSITIVE_INT.max(settings.baseStatMaxValue), + }), + [settings.baseStatMaxValue], + ); const { canClose, getFormData, onInputTouched, defaults, getRawFormData, formRef } = useZodForm(STATS_EDITOR_SCHEMA, form); const { Input } = useInputAttrsWithLabel(STATS_EDITOR_SCHEMA, defaults); - + const [showRecommendedWarning, setShowRecommendedWarning] = useState(isAboveRecommended(defaults)); const handleBaseStatChange = () => { if (totalRef.current) totalRef.current.innerText = `${calculateTotal(defaults, getRawFormData)}`; + setShowRecommendedWarning(isAboveRecommended(getRawFormData())); }; const onClose = () => { @@ -66,6 +94,7 @@ export const StatEditor = forwardRef((_, ref) => { + {showRecommendedWarning && {t('base_stat_recommended_limit_warning')}} From 76fb1c1032e1a24558e9f0414779d7462c9b7181 Mon Sep 17 00:00:00 2001 From: Green00101 <2585640230@qq.com> Date: Tue, 28 Apr 2026 14:33:22 +1000 Subject: [PATCH 2/2] Address base stat limit review comments --- assets/i18n/fr.json | 6 +++--- src/migrations/addBattleCamera3dToSettings.ts | 9 +++++---- src/migrations/addSummaryPagesOptionsToSettings.ts | 14 +++++++++----- src/migrations/migrationConfig.ts | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index 761d0ee47..a1924facb 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -1865,7 +1865,7 @@ "invalid_data": "Données invalides", "event_title_empty_state": "Commencez à créer un événement !", "event_description_empty_state": "Vous n'avez pas encore d'événement. Vous pouvez créer un nouvel événement ou importer des événements existants.", - "max_base_stat_value": "Max base stat value", - "base_stat_recommended_limit_warning": "Setting base stats above 255 may affect game balance or compatibility with existing data.", - "add_base_stat_max_value_to_settings": "Adding the max base stat value to project settings" + "max_base_stat_value": "Valeur maximum d'une statistique de base", + "base_stat_recommended_limit_warning": "Configurer une statistique avec une valeur supérieure à 255 peut affecter l'équilibrage du jeu ou provoquer des conflits avec les données existantes.", + "add_base_stat_max_value_to_settings": "Ajout du paramètre de statistique de base maximum aux paramètres du projet" } diff --git a/src/migrations/addBattleCamera3dToSettings.ts b/src/migrations/addBattleCamera3dToSettings.ts index 397a8c1cc..6dce8b36a 100644 --- a/src/migrations/addBattleCamera3dToSettings.ts +++ b/src/migrations/addBattleCamera3dToSettings.ts @@ -1,14 +1,15 @@ +import { SETTINGS_CONFIG_VALIDATOR, StudioSettingConfig } from '@modelEntities/config'; +import { parseJSON } from '@utils/json/parse'; import { IpcMainEvent } from 'electron'; -import path from 'path'; import fsPromise from 'fs/promises'; -import { parseJSON } from '@utils/json/parse'; -import { SETTINGS_CONFIG_VALIDATOR, StudioSettingConfig } from '@modelEntities/config'; +import path from 'path'; import { deletePSDKDatFile } from './migrateUtils'; const PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR = SETTINGS_CONFIG_VALIDATOR.omit({ isUseBattleCamera3d: true, showContestSummaryPage: true, showRibbonsSummaryPage: true, + baseStatMaxValue: true, }); export const addBattleCamera3dToSettings = async (_: IpcMainEvent, projectPath: string) => { @@ -20,7 +21,7 @@ export const addBattleCamera3dToSettings = async (_: IpcMainEvent, projectPath: const settingsFileParsed = PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR.safeParse(parseJSON(settingsFile, 'settings_config.json')); if (!settingsFileParsed.success) throw new Error('Fail to parse settings_config.json file'); - const newSettingsFile: Omit = { + const newSettingsFile: Omit = { ...settingsFileParsed.data, isUseBattleCamera3d: false, }; diff --git a/src/migrations/addSummaryPagesOptionsToSettings.ts b/src/migrations/addSummaryPagesOptionsToSettings.ts index eadd7db55..10f81bd5b 100644 --- a/src/migrations/addSummaryPagesOptionsToSettings.ts +++ b/src/migrations/addSummaryPagesOptionsToSettings.ts @@ -1,11 +1,15 @@ +import { SETTINGS_CONFIG_VALIDATOR, StudioSettingConfig } from '@modelEntities/config'; +import { parseJSON } from '@utils/json/parse'; import { IpcMainEvent } from 'electron'; -import path from 'path'; import fsPromise from 'fs/promises'; -import { parseJSON } from '@utils/json/parse'; -import { SETTINGS_CONFIG_VALIDATOR, StudioSettingConfig } from '@modelEntities/config'; +import path from 'path'; import { deletePSDKDatFile } from './migrateUtils'; -const PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR = SETTINGS_CONFIG_VALIDATOR.omit({ showContestSummaryPage: true, showRibbonsSummaryPage: true }); +const PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR = SETTINGS_CONFIG_VALIDATOR.omit({ + showContestSummaryPage: true, + showRibbonsSummaryPage: true, + baseStatMaxValue: true, +}); export const addSummaryPagesOptionsToSettings = async (_: IpcMainEvent, projectPath: string) => { deletePSDKDatFile(projectPath); @@ -16,7 +20,7 @@ export const addSummaryPagesOptionsToSettings = async (_: IpcMainEvent, projectP const settingsFileParsed = PRE_MIGRATION_SETTINGS_CONFIG_VALIDATOR.safeParse(parseJSON(settingsFile, 'settings_config.json')); if (!settingsFileParsed.success) throw new Error('Fail to parse settings_config.json file'); - const newSettingsFile: StudioSettingConfig = { + const newSettingsFile: Omit = { ...settingsFileParsed.data, showContestSummaryPage: true, showRibbonsSummaryPage: true, diff --git a/src/migrations/migrationConfig.ts b/src/migrations/migrationConfig.ts index cd39c9881..1df728b00 100644 --- a/src/migrations/migrationConfig.ts +++ b/src/migrations/migrationConfig.ts @@ -188,7 +188,7 @@ export const MIGRATION_CONFIG: MigrateConfigType[] = [ }, { migration: addBaseStatMaxValueToSettings, - version: '2.9.2', + version: '2.9.1', message: 'add_base_stat_max_value_to_settings', }, ];