diff --git a/public/images/items.json b/public/images/items.json index 86792e23cc0..52afefa8bf7 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -4,8 +4,8 @@ "image": "items.png", "format": "RGBA8888", "size": { - "w": 423, - "h": 423 + "w": 444, + "h": 444 }, "scale": 1, "frames": [ @@ -8387,6 +8387,27 @@ "w": 16, "h": 16 } + }, + { + "filename": "ultranecrozium_z", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 1, + "y": 8, + "w": 30, + "h": 15 + }, + "frame": { + "x": 414, + "y": 429, + "w": 30, + "h": 15 + } } ] } diff --git a/public/images/items.png b/public/images/items.png index 831576e45af..fb3029a4ee6 100644 Binary files a/public/images/items.png and b/public/images/items.png differ diff --git a/public/images/items/ultranecrozium_z.png b/public/images/items/ultranecrozium_z.png new file mode 100644 index 00000000000..208f3fb173d Binary files /dev/null and b/public/images/items/ultranecrozium_z.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 46996e9d0d2..656af1391fc 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -37,7 +37,7 @@ import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import { addUiThemeOverrides } from "./ui/ui-theme"; import PokemonData from "./system/pokemon-data"; import { Nature } from "./data/nature"; -import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from "./data/pokemon-forms"; +import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem } from "./data/pokemon-forms"; import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase"; import { getTypeRgb } from "./data/type"; import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; @@ -2576,7 +2576,23 @@ export default class BattleScene extends SceneBase { triggerPokemonFormChange(pokemon: Pokemon, formChangeTriggerType: Constructor, delayed: boolean = false, modal: boolean = false): boolean { if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId)) { - const matchingFormChange = pokemonFormChanges[pokemon.species.speciesId].find(fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon)); + + // in case this is NECROZMA, determine which forms this + const matchingFormChangeOpts = pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon)); + let matchingFormChange; + if (pokemon.species.speciesId === Species.NECROZMA && matchingFormChangeOpts.length > 1) { + // Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves. + const formChangeItemModifiers = (this.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]).filter(m => m.active).map(m => m.formChangeItem); + + + matchingFormChange = formChangeItemModifiers.includes(FormChangeItem.N_LUNARIZER) ? + matchingFormChangeOpts[0] : + formChangeItemModifiers.includes(FormChangeItem.N_SOLARIZER) ? + matchingFormChangeOpts[1] : + null; + } else { + matchingFormChange = matchingFormChangeOpts[0]; + } if (matchingFormChange) { let phase: Phase; if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) { diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index c4cf4184f57..3db44310c19 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -90,6 +90,7 @@ export enum FormChangeItem { BURN_DRIVE, CHILL_DRIVE, DOUSE_DRIVE, + ULTRANECROZIUM_Z, FIST_PLATE = 100, SKY_PLATE, @@ -714,7 +715,9 @@ export const pokemonFormChanges: PokemonFormChanges = { ], [Species.NECROZMA]: [ new SpeciesFormChange(Species.NECROZMA, "", "dawn-wings", new SpeciesFormChangeItemTrigger(FormChangeItem.N_LUNARIZER), false, getSpeciesDependentFormChangeCondition(Species.LUNALA)), - new SpeciesFormChange(Species.NECROZMA, "", "dusk-mane", new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER), false, getSpeciesDependentFormChangeCondition(Species.SOLGALEO)) + new SpeciesFormChange(Species.NECROZMA, "", "dusk-mane", new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER), false, getSpeciesDependentFormChangeCondition(Species.SOLGALEO)), + new SpeciesFormChange(Species.NECROZMA, "dawn-wings", "ultra", new SpeciesFormChangeItemTrigger(FormChangeItem.ULTRANECROZIUM_Z)), + new SpeciesFormChange(Species.NECROZMA, "dusk-mane", "ultra", new SpeciesFormChangeItemTrigger(FormChangeItem.ULTRANECROZIUM_Z)) ], [Species.MELMETAL]: [ new SpeciesFormChange(Species.MELMETAL, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)) diff --git a/src/locales/ca_ES/modifier-type.ts b/src/locales/ca_ES/modifier-type.ts index c6c98e44a92..68761c33106 100644 --- a/src/locales/ca_ES/modifier-type.ts +++ b/src/locales/ca_ES/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "Burn Drive", "CHILL_DRIVE": "Chill Drive", "DOUSE_DRIVE": "Douse Drive", + "ULTRANECROZIUM_Z": "Ultranecrozium Z", "FIST_PLATE": "Fist Plate", "SKY_PLATE": "Sky Plate", diff --git a/src/locales/de/modifier-type.ts b/src/locales/de/modifier-type.ts index 90ac9413466..d9b1553fd18 100644 --- a/src/locales/de/modifier-type.ts +++ b/src/locales/de/modifier-type.ts @@ -416,6 +416,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "Flammenmodul", "CHILL_DRIVE": "Gefriermodul", "DOUSE_DRIVE": "Aquamodul", + "ULTRANECROZIUM_Z": "Ultranecrozium Z", "FIST_PLATE": "Fausttafel", "SKY_PLATE": "Wolkentafel", diff --git a/src/locales/en/modifier-type.ts b/src/locales/en/modifier-type.ts index c6c98e44a92..68761c33106 100644 --- a/src/locales/en/modifier-type.ts +++ b/src/locales/en/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "Burn Drive", "CHILL_DRIVE": "Chill Drive", "DOUSE_DRIVE": "Douse Drive", + "ULTRANECROZIUM_Z": "Ultranecrozium Z", "FIST_PLATE": "Fist Plate", "SKY_PLATE": "Sky Plate", diff --git a/src/locales/es/modifier-type.ts b/src/locales/es/modifier-type.ts index d2bd44a0f06..fc3781d303b 100644 --- a/src/locales/es/modifier-type.ts +++ b/src/locales/es/modifier-type.ts @@ -413,6 +413,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "PiroROM", "CHILL_DRIVE": "CrioROM", "DOUSE_DRIVE": "HidroROM", + "ULTRANECROZIUM_Z": "Ultranecrostal Z", "FIST_PLATE": "Tabla Fuerte", "SKY_PLATE": "Tabla Cielo", diff --git a/src/locales/fr/modifier-type.ts b/src/locales/fr/modifier-type.ts index 0a472b9f0d9..1c287991329 100644 --- a/src/locales/fr/modifier-type.ts +++ b/src/locales/fr/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "Module Pyro", "CHILL_DRIVE": "Module Cryo", "DOUSE_DRIVE": "Module Aqua", + "ULTRANECROZIUM_Z": "Ultranécrozélite", "FIST_PLATE": "Plaque Poing", "SKY_PLATE": "Plaque Ciel", diff --git a/src/locales/it/modifier-type.ts b/src/locales/it/modifier-type.ts index 618d19395e5..6f054e4e566 100644 --- a/src/locales/it/modifier-type.ts +++ b/src/locales/it/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "Piromodulo", "CHILL_DRIVE": "Gelomodulo", "DOUSE_DRIVE": "Idromodulo", + "ULTRANECROZIUM_Z": "Ultranecrozium Z", "FIST_PLATE": "Lastrapugno", "SKY_PLATE": "Lastracielo", diff --git a/src/locales/ja/modifier-type.ts b/src/locales/ja/modifier-type.ts index e9a28a63af8..39efe5ed071 100644 --- a/src/locales/ja/modifier-type.ts +++ b/src/locales/ja/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "ブレイズカセット", "CHILL_DRIVE": "フリーズカセット", "DOUSE_DRIVE": "アクアカセット", + "ULTRANECROZIUM_Z": "ウルトラネクロZ", "FIST_PLATE": "Fist Plate", "SKY_PLATE": "Sky Plate", diff --git a/src/locales/ko/modifier-type.ts b/src/locales/ko/modifier-type.ts index f09fba0b09a..9138600c2eb 100644 --- a/src/locales/ko/modifier-type.ts +++ b/src/locales/ko/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "블레이즈카세트", "CHILL_DRIVE": "프리즈카세트", "DOUSE_DRIVE": "아쿠아카세트", + "ULTRANECROZIUM_Z": "울트라네크로Z", "FIST_PLATE": "주먹플레이트", "SKY_PLATE": "푸른하늘플레이트", diff --git a/src/locales/pt_BR/modifier-type.ts b/src/locales/pt_BR/modifier-type.ts index a5cddaed235..d907784a443 100644 --- a/src/locales/pt_BR/modifier-type.ts +++ b/src/locales/pt_BR/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "IgneDisco", "CHILL_DRIVE": "CrioDisco", "DOUSE_DRIVE": "HidroDisco", + "ULTRANECROZIUM_Z": "Ultranecrozium Z", "FIST_PLATE": "Placa do Punho", "SKY_PLATE": "Placa do Céu", diff --git a/src/locales/zh_CN/modifier-type.ts b/src/locales/zh_CN/modifier-type.ts index 71f5b21ba81..bd87f7d8254 100644 --- a/src/locales/zh_CN/modifier-type.ts +++ b/src/locales/zh_CN/modifier-type.ts @@ -414,6 +414,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "BURN_DRIVE": "火焰卡带", "CHILL_DRIVE": "冰冻卡带", "DOUSE_DRIVE": "水流卡带", + "ULTRANECROZIUM_Z": "究极奈克洛Z", "FIST_PLATE": "拳头石板", "SKY_PLATE": "蓝天石板", diff --git a/src/locales/zh_TW/modifier-type.ts b/src/locales/zh_TW/modifier-type.ts index 3d56deca3d7..ccec2c96bb2 100644 --- a/src/locales/zh_TW/modifier-type.ts +++ b/src/locales/zh_TW/modifier-type.ts @@ -477,6 +477,7 @@ export const modifierType: ModifierTypeTranslationEntries = { BURN_DRIVE: "火焰卡帶", CHILL_DRIVE: "冰凍卡帶", DOUSE_DRIVE: "水流卡帶", + ULTRANECROZIUM_Z: "究極奈克洛Z", "FIST_PLATE": "拳頭石板", "SKY_PLATE": "藍天石板", diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 16a74757598..56504415cf6 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1049,14 +1049,41 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { return new FormChangeItemModifierType(pregenArgs[0] as FormChangeItem); } - const formChangeItemPool = party.filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)).map(p => { + const formChangeItemPool = [...new Set(party.filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)).map(p => { const formChanges = pokemonFormChanges[p.species.speciesId]; - return formChanges.filter(fc => ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || party[0].scene.getModifiers(Modifiers.MegaEvolutionAccessModifier).length) + let formChangeItemTriggers = formChanges.filter(fc => ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || party[0].scene.getModifiers(Modifiers.MegaEvolutionAccessModifier).length) && ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || party[0].scene.getModifiers(Modifiers.GigantamaxAccessModifier).length) && (!fc.conditions.length || fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p)).length)) .map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) .filter(t => t && t.active && !p.scene.findModifier(m => m instanceof Modifiers.PokemonFormChangeItemModifier && m.pokemonId === p.id && m.formChangeItem === t.item)); - }).flat().flatMap(fc => fc.item); + + if (p.species.speciesId === Species.NECROZMA) { + // technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break... + + let foundULTRA_Z = false, + foundN_LUNA = false, + foundN_SOLAR = false; + formChangeItemTriggers.forEach((fc, i) => { + switch (fc.item) { + case FormChangeItem.ULTRANECROZIUM_Z: + foundULTRA_Z = true; + break; + case FormChangeItem.N_LUNARIZER: + foundN_LUNA = true; + break; + case FormChangeItem.N_SOLARIZER: + foundN_SOLAR = true; + break; + } + }); + if (foundULTRA_Z && foundN_LUNA && foundN_SOLAR) { + // all three items are present -> user hasn't acquired any of the N_*ARIZERs -> block ULTRANECROZIUM_Z acquisition. + formChangeItemTriggers = formChangeItemTriggers.filter(fc => fc.item !== FormChangeItem.ULTRANECROZIUM_Z); + } + } + return formChangeItemTriggers; + }).flat().flatMap(fc => fc.item))]; + // convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party. if (!formChangeItemPool.length) { return null; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 4dbb34fdc83..c34b19d9471 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,6 +1,6 @@ import { CommandPhase, SelectModifierPhase } from "../phases"; import BattleScene from "../battle-scene"; -import { MoveResult, PlayerPokemon, PokemonMove } from "../field/pokemon"; +import Pokemon, { MoveResult, PlayerPokemon, PokemonMove } from "../field/pokemon"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; import MessageUiHandler from "./message-ui-handler"; @@ -13,7 +13,7 @@ import { StatusEffect } from "../data/status-effect"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import { pokemonEvolutions } from "../data/pokemon-evolutions"; import { addWindow } from "./ui-theme"; -import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; +import { SpeciesFormChangeItemTrigger, FormChangeItem } from "../data/pokemon-forms"; import { getVariantTint } from "#app/data/variant"; import {Button} from "#enums/buttons"; import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; @@ -21,6 +21,7 @@ import MoveInfoOverlay from "./move-info-overlay"; import i18next from "i18next"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import { getPokemonNameWithAffix } from "#app/messages.js"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); @@ -433,10 +434,7 @@ export default class PartyUiHandler extends MessageUiHandler { } else { if (option >= PartyOption.FORM_CHANGE_ITEM && this.scene.getCurrentPhase() instanceof SelectModifierPhase) { if (this.partyUiMode === PartyUiMode.CHECK) { - let formChangeItemModifiers = this.scene.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]; - if (formChangeItemModifiers.find(m => m.active)) { - formChangeItemModifiers = formChangeItemModifiers.filter(m => m.active); - } + const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; modifier.active = !modifier.active; this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true); @@ -863,10 +861,7 @@ export default class PartyUiHandler extends MessageUiHandler { break; case PartyUiMode.CHECK: if (this.scene.getCurrentPhase() instanceof SelectModifierPhase) { - formChangeItemModifiers = this.scene.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]; - if (formChangeItemModifiers.find(m => m.active)) { - formChangeItemModifiers = formChangeItemModifiers.filter(m => m.active); - } + formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); for (let i = 0; i < formChangeItemModifiers.length; i++) { this.options.push(PartyOption.FORM_CHANGE_ITEM + i); } @@ -1091,6 +1086,23 @@ export default class PartyUiHandler extends MessageUiHandler { } } + getFormChangeItemsModifiers(pokemon: Pokemon) { + let formChangeItemModifiers = this.scene.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]; + const ultraNecrozmaModifiers = formChangeItemModifiers.filter(m => m.active && m.formChangeItem === FormChangeItem.ULTRANECROZIUM_Z); + if (ultraNecrozmaModifiers.length > 0) { + // ULTRANECROZIUM_Z is active and deactivating it should be the only option + return ultraNecrozmaModifiers; + } + if (formChangeItemModifiers.find(m => m.active)) { + // a form is currently active. the user has to disable the form or activate ULTRANECROZIUM_Z + formChangeItemModifiers = formChangeItemModifiers.filter(m => m.active || m.formChangeItem === FormChangeItem.ULTRANECROZIUM_Z); + } else if (pokemon.species.speciesId === Species.NECROZMA) { + // no form is currently active. the user has to activate some form, except ULTRANECROZIUM_Z + formChangeItemModifiers = formChangeItemModifiers.filter(m => m.formChangeItem !== FormChangeItem.ULTRANECROZIUM_Z); + } + return formChangeItemModifiers; + } + getOptionsCursorWithScroll(): integer { return this.optionsCursor + this.optionsScrollCursor + (this.options && this.options[0] === PartyOption.SCROLL_UP ? -1 : 0); }