Merge remote-tracking branch 'upstream/beta' into type-move
14
biome.jsonc
@ -32,7 +32,6 @@
|
|||||||
// TODO: these files are too big and complex, ignore them until their respective refactors
|
// TODO: these files are too big and complex, ignore them until their respective refactors
|
||||||
"src/data/moves/move.ts",
|
"src/data/moves/move.ts",
|
||||||
"src/data/abilities/ability.ts",
|
"src/data/abilities/ability.ts",
|
||||||
"src/field/pokemon.ts",
|
|
||||||
|
|
||||||
// this file is just too big:
|
// this file is just too big:
|
||||||
"src/data/balance/tms.ts"
|
"src/data/balance/tms.ts"
|
||||||
@ -92,6 +91,19 @@
|
|||||||
"noUselessSwitchCase": "off", // Explicit > Implicit
|
"noUselessSwitchCase": "off", // Explicit > Implicit
|
||||||
"noUselessConstructor": "warn", // TODO: Refactor and make this an error
|
"noUselessConstructor": "warn", // TODO: Refactor and make this an error
|
||||||
"noBannedTypes": "warn" // TODO: Refactor and make this an error
|
"noBannedTypes": "warn" // TODO: Refactor and make this an error
|
||||||
|
},
|
||||||
|
"nursery": {
|
||||||
|
"noRestrictedTypes": {
|
||||||
|
"level": "error",
|
||||||
|
"options": {
|
||||||
|
"types": {
|
||||||
|
"integer": {
|
||||||
|
"message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.",
|
||||||
|
"use": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
@ -1,34 +1,36 @@
|
|||||||
{
|
{
|
||||||
"1": {
|
"1": {
|
||||||
"319452": "831a1f",
|
|
||||||
"4a7310": "982443",
|
|
||||||
"7ba563": "b44040",
|
|
||||||
"bdef84": "ec8c8c",
|
|
||||||
"8cbd63": "c54b4b",
|
"8cbd63": "c54b4b",
|
||||||
"215200": "710f2e",
|
"a5d670": "df5252",
|
||||||
|
"4aa552": "9f2f2c",
|
||||||
"a5d674": "e16363",
|
"a5d674": "e16363",
|
||||||
"196b21": "891222",
|
"7aa953": "c54b4b",
|
||||||
|
"7ba563": "b44040",
|
||||||
|
"215200": "710f2e",
|
||||||
"f7ce00": "7aa1df",
|
"f7ce00": "7aa1df",
|
||||||
"525252": "123a5a",
|
"525252": "123a5a",
|
||||||
"63b56b": "b2332f",
|
|
||||||
"a5d673": "df5252",
|
|
||||||
"8c6b3a": "448bc3",
|
"8c6b3a": "448bc3",
|
||||||
"4aa552": "9f2f2c"
|
"bdef84": "ec8c8c",
|
||||||
|
"63b56b": "b2332f",
|
||||||
|
"319452": "831a1f",
|
||||||
|
"196b21": "891222",
|
||||||
|
"4a7310": "982443"
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
"319452": "b08d72",
|
|
||||||
"4a7310": "4f3956",
|
|
||||||
"7ba563": "704e7e",
|
|
||||||
"bdef84": "a779ba",
|
|
||||||
"8cbd63": "e3d7a6",
|
"8cbd63": "e3d7a6",
|
||||||
"215200": "583823",
|
"a5d670": "d7cda7",
|
||||||
|
"4aa552": "c5a77f",
|
||||||
"a5d674": "8c669b",
|
"a5d674": "8c669b",
|
||||||
"196b21": "78582c",
|
"7aa953": "704e7e",
|
||||||
|
"7ba563": "704e7e",
|
||||||
|
"215200": "583823",
|
||||||
"f7ce00": "f2aacd",
|
"f7ce00": "f2aacd",
|
||||||
"525252": "a53b6f",
|
"525252": "a53b6f",
|
||||||
"63b56b": "cfc191",
|
|
||||||
"a5d673": "d7cda7",
|
|
||||||
"8c6b3a": "df87bb",
|
"8c6b3a": "df87bb",
|
||||||
"4aa552": "c5a77f"
|
"bdef84": "a779ba",
|
||||||
|
"63b56b": "cfc191",
|
||||||
|
"319452": "b08d72",
|
||||||
|
"196b21": "78582c",
|
||||||
|
"4a7310": "4f3956"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,28 +1,28 @@
|
|||||||
{
|
{
|
||||||
"1": {
|
"1": {
|
||||||
|
"196b21": "831a1f",
|
||||||
|
"7ba563": "b44040",
|
||||||
|
"215201": "630d28",
|
||||||
|
"215200": "710f2f",
|
||||||
|
"a5d674": "df5252",
|
||||||
|
"8cbd63": "c54b4b",
|
||||||
|
"63b56b": "b2332f",
|
||||||
|
"a5d670": "e16363",
|
||||||
"319452": "831a1f",
|
"319452": "831a1f",
|
||||||
"4aa552": "9f2f2c",
|
"4aa552": "9f2f2c",
|
||||||
"7ba563": "b44040",
|
"4a7310": "982443"
|
||||||
"8cbd63": "c54b4b",
|
|
||||||
"215200": "710f2f",
|
|
||||||
"196b21": "831a1f",
|
|
||||||
"a5d674": "df5252",
|
|
||||||
"4a7310": "982443",
|
|
||||||
"a5d673": "e16363",
|
|
||||||
"63b56b": "b2332f",
|
|
||||||
"215201": "630d28"
|
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
|
"196b21": "b08d72",
|
||||||
|
"7ba563": "704e7e",
|
||||||
|
"215201": "583823",
|
||||||
|
"215200": "3f3249",
|
||||||
|
"a5d674": "d7cda7",
|
||||||
|
"8cbd63": "e3d7a6",
|
||||||
|
"63b56b": "cfc191",
|
||||||
|
"a5d670": "8c669b",
|
||||||
"319452": "b08d72",
|
"319452": "b08d72",
|
||||||
"4aa552": "c5a77f",
|
"4aa552": "c5a77f",
|
||||||
"7ba563": "704e7e",
|
"4a7310": "4f3956"
|
||||||
"8cbd63": "e3d7a6",
|
|
||||||
"215200": "3f3249",
|
|
||||||
"196b21": "b08d72",
|
|
||||||
"a5d674": "d7cda7",
|
|
||||||
"4a7310": "4f3956",
|
|
||||||
"a5d673": "8c669b",
|
|
||||||
"63b56b": "cfc191",
|
|
||||||
"215201": "583823"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,28 +1,28 @@
|
|||||||
{
|
{
|
||||||
"1": {
|
"1": {
|
||||||
|
"196b21": "780d4a",
|
||||||
|
"7ba563": "b44040",
|
||||||
|
"215201": "710f2e",
|
||||||
|
"215200": "710f2f",
|
||||||
|
"a5d674": "de5b6f",
|
||||||
|
"8cbd63": "bf3d64",
|
||||||
|
"63b56b": "9e2056",
|
||||||
|
"a5d670": "e16363",
|
||||||
"319452": "780d4a",
|
"319452": "780d4a",
|
||||||
"4aa552": "8a1652",
|
"4aa552": "8a1652",
|
||||||
"7ba563": "b44040",
|
"4a7310": "982443"
|
||||||
"8cbd63": "bf3d64",
|
|
||||||
"215200": "710f2f",
|
|
||||||
"196b21": "780d4a",
|
|
||||||
"a5d674": "de5b6f",
|
|
||||||
"4a7310": "982443",
|
|
||||||
"a5d673": "e16363",
|
|
||||||
"63b56b": "9e2056",
|
|
||||||
"215201": "710f2e"
|
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
|
"196b21": "b59c72",
|
||||||
|
"7ba563": "805a9c",
|
||||||
|
"215201": "694d37",
|
||||||
|
"215200": "41334d",
|
||||||
|
"a5d674": "f6f7df",
|
||||||
|
"8cbd63": "ebe9ca",
|
||||||
|
"63b56b": "e3ddb8",
|
||||||
|
"a5d670": "a473ba",
|
||||||
"319452": "b59c72",
|
"319452": "b59c72",
|
||||||
"4aa552": "c9b991",
|
"4aa552": "c9b991",
|
||||||
"7ba563": "805a9c",
|
"4a7310": "4f3956"
|
||||||
"8cbd63": "ebe9ca",
|
|
||||||
"215200": "41334d",
|
|
||||||
"196b21": "b59c72",
|
|
||||||
"a5d674": "f6f7df",
|
|
||||||
"4a7310": "4f3956",
|
|
||||||
"a5d673": "a473ba",
|
|
||||||
"63b56b": "e3ddb8",
|
|
||||||
"215201": "694d37"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,34 +1,36 @@
|
|||||||
{
|
{
|
||||||
"1": {
|
"1": {
|
||||||
"319452": "780d4a",
|
|
||||||
"4a7310": "982443",
|
|
||||||
"7ba563": "b44040",
|
|
||||||
"bdef84": "ec8c8c",
|
|
||||||
"8cbd63": "bf3d64",
|
"8cbd63": "bf3d64",
|
||||||
"215200": "710f2e",
|
"a5d670": "de5b6f",
|
||||||
|
"4aa552": "8a1652",
|
||||||
"a5d674": "e16363",
|
"a5d674": "e16363",
|
||||||
"196b21": "7d1157",
|
"7aa953": "bf3d64",
|
||||||
|
"7ba563": "b44040",
|
||||||
|
"215200": "710f2e",
|
||||||
"f7ce00": "5bcfc3",
|
"f7ce00": "5bcfc3",
|
||||||
"525252": "20668c",
|
"525252": "20668c",
|
||||||
"63b56b": "9e2056",
|
|
||||||
"a5d673": "de5b6f",
|
|
||||||
"8c6b3a": "33a3b0",
|
"8c6b3a": "33a3b0",
|
||||||
"4aa552": "8a1652"
|
"bdef84": "ec8c8c",
|
||||||
|
"63b56b": "9e2056",
|
||||||
|
"319452": "780d4a",
|
||||||
|
"196b21": "7d1157",
|
||||||
|
"4a7310": "982443"
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
"319452": "b59c72",
|
|
||||||
"4a7310": "4f3956",
|
|
||||||
"7ba563": "805a9c",
|
|
||||||
"bdef84": "c193cf",
|
|
||||||
"8cbd63": "f6f7df",
|
"8cbd63": "f6f7df",
|
||||||
"215200": "694d37",
|
"a5d670": "ebe9ca",
|
||||||
|
"4aa552": "c9b991",
|
||||||
"a5d674": "a473ba",
|
"a5d674": "a473ba",
|
||||||
"196b21": "9c805f",
|
"7aa953": "805a9c",
|
||||||
|
"7ba563": "805a9c",
|
||||||
|
"215200": "694d37",
|
||||||
"f7ce00": "f2aab6",
|
"f7ce00": "f2aab6",
|
||||||
"525252": "983364",
|
"525252": "983364",
|
||||||
"63b56b": "e3ddb8",
|
|
||||||
"a5d673": "ebe9ca",
|
|
||||||
"8c6b3a": "df879f",
|
"8c6b3a": "df879f",
|
||||||
"4aa552": "c9b991"
|
"bdef84": "c193cf",
|
||||||
|
"63b56b": "e3ddb8",
|
||||||
|
"319452": "b59c72",
|
||||||
|
"196b21": "9c805f",
|
||||||
|
"4a7310": "4f3956"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
Subproject commit 42cd5cf577f475c22bc82d55e7ca358eb4f3184f
|
Subproject commit e9ccbadb6eaa3b797f3dec919745befda2ec74bd
|
149
scripts/decrypt-save.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import pkg from "crypto-js";
|
||||||
|
const { AES, enc } = pkg;
|
||||||
|
// biome-ignore lint: This is how you import fs from node
|
||||||
|
import * as fs from "node:fs";
|
||||||
|
|
||||||
|
const SAVE_KEY = "x0i2O7WRiANTqPmZ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of condensed keynames to their associated full names
|
||||||
|
* NOTE: Update this if `src/system/game-data#systemShortKeys` ever changes!
|
||||||
|
*/
|
||||||
|
const systemShortKeys = {
|
||||||
|
seenAttr: "$sa",
|
||||||
|
caughtAttr: "$ca",
|
||||||
|
natureAttr: "$na",
|
||||||
|
seenCount: "$s",
|
||||||
|
caughtCount: "$c",
|
||||||
|
hatchedCount: "$hc",
|
||||||
|
ivs: "$i",
|
||||||
|
moveset: "$m",
|
||||||
|
eggMoves: "$em",
|
||||||
|
candyCount: "$x",
|
||||||
|
friendship: "$f",
|
||||||
|
abilityAttr: "$a",
|
||||||
|
passiveAttr: "$pa",
|
||||||
|
valueReduction: "$vr",
|
||||||
|
classicWinCount: "$wc",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the shortened key names with their full names
|
||||||
|
* @param {string} dataStr - The string to convert
|
||||||
|
* @returns {string} The string with shortened keynames replaced with full names
|
||||||
|
*/
|
||||||
|
function convertSystemDataStr(dataStr) {
|
||||||
|
const fromKeys = Object.values(systemShortKeys);
|
||||||
|
const toKeys = Object.keys(systemShortKeys);
|
||||||
|
for (const k in fromKeys) {
|
||||||
|
dataStr = dataStr.replace(new RegExp(`${fromKeys[k].replace("$", "\\$")}`, "g"), toKeys[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a save
|
||||||
|
* @param {string} path - The path to the encrypted save file
|
||||||
|
* @returns {string} The decrypted save data
|
||||||
|
*/
|
||||||
|
function decryptSave(path) {
|
||||||
|
// Check if the file exists
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
console.error(`File not found: ${path}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
let fileData;
|
||||||
|
try {
|
||||||
|
fileData = fs.readFileSync(path, "utf8");
|
||||||
|
} catch (e) {
|
||||||
|
switch (e.code) {
|
||||||
|
case "ENOENT":
|
||||||
|
console.error(`File not found: ${path}`);
|
||||||
|
break;
|
||||||
|
case "EACCES":
|
||||||
|
console.error(`Could not open ${path}: Permission denied`);
|
||||||
|
break;
|
||||||
|
case "EISDIR":
|
||||||
|
console.error(`Unable to read ${path} as it is a directory`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Error reading file: ${e.message}`);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
return convertSystemDataStr(AES.decrypt(fileData, SAVE_KEY).toString(enc.Utf8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print the usage message and exits */
|
||||||
|
function printUsage() {
|
||||||
|
console.log(`
|
||||||
|
Usage: node decrypt-save.js <encrypted-file> [save-file]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
file-path Path to the encrypted save file to decrypt.
|
||||||
|
save-file Path to where the decrypted data should be written. If not provided, the decrypted data will be printed to the console.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Show this help message and exit.
|
||||||
|
|
||||||
|
Description:
|
||||||
|
This script decrypts an encrypted pokerogue save file
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write `data` to `filePath`, gracefully communicating errors that arise
|
||||||
|
* @param {string} filePath
|
||||||
|
* @param {string} data
|
||||||
|
*/
|
||||||
|
function writeToFile(filePath, data) {
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(filePath, data);
|
||||||
|
} catch (e) {
|
||||||
|
switch (e.code) {
|
||||||
|
case "EACCES":
|
||||||
|
console.error(`Could not open ${filePath}: Permission denied`);
|
||||||
|
break;
|
||||||
|
case "EISDIR":
|
||||||
|
console.error(`Unable to write to ${filePath} as it is a directory`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Error writing file: ${e.message}`);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
let args = process.argv.slice(2);
|
||||||
|
// Get options
|
||||||
|
const options = args.filter(arg => arg.startsWith("-"));
|
||||||
|
// get args
|
||||||
|
args = args.filter(arg => !arg.startsWith("-"));
|
||||||
|
|
||||||
|
if (args.length === 0 || options.includes("-h") || options.includes("--help") || args.length > 2) {
|
||||||
|
printUsage();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
// If the user provided a second argument, check if the file exists already and refuse to write to it.
|
||||||
|
if (args.length === 2) {
|
||||||
|
const destPath = args[1];
|
||||||
|
if (fs.existsSync(destPath)) {
|
||||||
|
console.error(`Refusing to overwrite ${destPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, commence decryption.
|
||||||
|
const decrypt = decryptSave(args[0]);
|
||||||
|
|
||||||
|
if (args.length === 1) {
|
||||||
|
process.stdout.write(decrypt);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeToFile(destPath, decrypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
@ -7522,7 +7522,7 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr {
|
|||||||
|
|
||||||
/** Causes the effect to fail when the target's ability is unsupressable or already suppressed. */
|
/** Causes the effect to fail when the target's ability is unsupressable or already suppressed. */
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user, target, move) => target.getAbility().isSuppressable && !target.summonData.abilitySuppressed;
|
return (_user, target, _move) => !target.summonData.abilitySuppressed && (target.getAbility().isSuppressable || (target.hasPassive() && target.getPassiveAbility().isSuppressable));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
updatePlayerMoney,
|
updatePlayerMoney,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { isNullOrUndefined, NumberHolder, randSeedInt, randSeedItem } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils/common";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -88,7 +88,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
|||||||
|
|
||||||
const r = randSeedInt(SHINY_MAGIKARP_WEIGHT);
|
const r = randSeedInt(SHINY_MAGIKARP_WEIGHT);
|
||||||
|
|
||||||
let validEventEncounters = timedEventManager
|
const validEventEncounters = timedEventManager
|
||||||
.getEventEncounters()
|
.getEventEncounters()
|
||||||
.filter(
|
.filter(
|
||||||
s =>
|
s =>
|
||||||
@ -111,22 +111,26 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
|||||||
if (
|
if (
|
||||||
r === 0 ||
|
r === 0 ||
|
||||||
((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) &&
|
((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) &&
|
||||||
(validEventEncounters.length === 0))
|
validEventEncounters.length === 0)
|
||||||
) {
|
) {
|
||||||
// If you roll 1%, give shiny Magikarp with random variant
|
// If you roll 1%, give shiny Magikarp with random variant
|
||||||
species = getPokemonSpecies(Species.MAGIKARP);
|
species = getPokemonSpecies(Species.MAGIKARP);
|
||||||
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
||||||
}
|
} else if (
|
||||||
else if (
|
validEventEncounters.length > 0 &&
|
||||||
(validEventEncounters.length > 0 && (r <= EVENT_THRESHOLD ||
|
(r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE)
|
||||||
(isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE)))
|
|
||||||
) {
|
) {
|
||||||
tries = 0;
|
tries = 0;
|
||||||
do {
|
do {
|
||||||
// If you roll 20%, give event encounter with 3 extra shiny rolls and its HA, if it has one
|
// If you roll 20%, give event encounter with 3 extra shiny rolls and its HA, if it has one
|
||||||
const enc = randSeedItem(validEventEncounters);
|
const enc = randSeedItem(validEventEncounters);
|
||||||
species = getPokemonSpecies(enc.species);
|
species = getPokemonSpecies(enc.species);
|
||||||
pokemon = new PlayerPokemon(species, 5, species.abilityHidden === Abilities.NONE ? undefined : 2, enc.formIndex);
|
pokemon = new PlayerPokemon(
|
||||||
|
species,
|
||||||
|
5,
|
||||||
|
species.abilityHidden === Abilities.NONE ? undefined : 2,
|
||||||
|
enc.formIndex,
|
||||||
|
);
|
||||||
pokemon.trySetShinySeed();
|
pokemon.trySetShinySeed();
|
||||||
pokemon.trySetShinySeed();
|
pokemon.trySetShinySeed();
|
||||||
pokemon.trySetShinySeed();
|
pokemon.trySetShinySeed();
|
||||||
@ -145,15 +149,13 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
|||||||
pokemon.trySetShinySeed();
|
pokemon.trySetShinySeed();
|
||||||
pokemon.trySetShinySeed();
|
pokemon.trySetShinySeed();
|
||||||
pokemon.trySetShinySeed();
|
pokemon.trySetShinySeed();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// If there's, and this would never happen, no eligible event encounters with a hidden ability, just do Magikarp
|
// If there's, and this would never happen, no eligible event encounters with a hidden ability, just do Magikarp
|
||||||
species = getPokemonSpecies(Species.MAGIKARP);
|
species = getPokemonSpecies(Species.MAGIKARP);
|
||||||
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
pokemon = new PlayerPokemon(species, 5, 2, species.formIndex);
|
pokemon = new PlayerPokemon(species, 5, 2, species.formIndex);
|
||||||
}
|
}
|
||||||
pokemon.generateAndPopulateMoveset();
|
pokemon.generateAndPopulateMoveset();
|
||||||
|
@ -1697,8 +1697,8 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(Species.CHINCHOU, 2, false, false, false, "Angler Pokémon", PokemonType.WATER, PokemonType.ELECTRIC, 0.5, 12, Abilities.VOLT_ABSORB, Abilities.ILLUMINATE, Abilities.WATER_ABSORB, 330, 75, 38, 38, 56, 56, 67, 190, 50, 66, GrowthRate.SLOW, 50, false),
|
new PokemonSpecies(Species.CHINCHOU, 2, false, false, false, "Angler Pokémon", PokemonType.WATER, PokemonType.ELECTRIC, 0.5, 12, Abilities.VOLT_ABSORB, Abilities.ILLUMINATE, Abilities.WATER_ABSORB, 330, 75, 38, 38, 56, 56, 67, 190, 50, 66, GrowthRate.SLOW, 50, false),
|
||||||
new PokemonSpecies(Species.LANTURN, 2, false, false, false, "Light Pokémon", PokemonType.WATER, PokemonType.ELECTRIC, 1.2, 22.5, Abilities.VOLT_ABSORB, Abilities.ILLUMINATE, Abilities.WATER_ABSORB, 460, 125, 58, 58, 76, 76, 67, 75, 50, 161, GrowthRate.SLOW, 50, false),
|
new PokemonSpecies(Species.LANTURN, 2, false, false, false, "Light Pokémon", PokemonType.WATER, PokemonType.ELECTRIC, 1.2, 22.5, Abilities.VOLT_ABSORB, Abilities.ILLUMINATE, Abilities.WATER_ABSORB, 460, 125, 58, 58, 76, 76, 67, 75, 50, 161, GrowthRate.SLOW, 50, false),
|
||||||
new PokemonSpecies(Species.PICHU, 2, false, false, false, "Tiny Mouse Pokémon", PokemonType.ELECTRIC, null, 0.3, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, GrowthRate.MEDIUM_FAST, 50, false, false,
|
new PokemonSpecies(Species.PICHU, 2, false, false, false, "Tiny Mouse Pokémon", PokemonType.ELECTRIC, null, 0.3, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||||
new PokemonForm("Normal", "", PokemonType.ELECTRIC, null, 1.4, 61.5, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
|
new PokemonForm("Normal", "", PokemonType.ELECTRIC, null, 1.4, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
|
||||||
new PokemonForm("Spiky-Eared", "spiky", PokemonType.ELECTRIC, null, 1.4, 61.5, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
|
new PokemonForm("Spiky-Eared", "spiky", PokemonType.ELECTRIC, null, 1.4, 2, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 205, 20, 40, 15, 35, 35, 60, 190, 70, 41, false, null, true),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.CLEFFA, 2, false, false, false, "Star Shape Pokémon", PokemonType.FAIRY, null, 0.3, 3, Abilities.CUTE_CHARM, Abilities.MAGIC_GUARD, Abilities.FRIEND_GUARD, 218, 50, 25, 28, 45, 55, 15, 150, 140, 44, GrowthRate.FAST, 25, false),
|
new PokemonSpecies(Species.CLEFFA, 2, false, false, false, "Star Shape Pokémon", PokemonType.FAIRY, null, 0.3, 3, Abilities.CUTE_CHARM, Abilities.MAGIC_GUARD, Abilities.FRIEND_GUARD, 218, 50, 25, 28, 45, 55, 15, 150, 140, 44, GrowthRate.FAST, 25, false),
|
||||||
new PokemonSpecies(Species.IGGLYBUFF, 2, false, false, false, "Balloon Pokémon", PokemonType.NORMAL, PokemonType.FAIRY, 0.3, 1, Abilities.CUTE_CHARM, Abilities.COMPETITIVE, Abilities.FRIEND_GUARD, 210, 90, 30, 15, 40, 20, 15, 170, 50, 42, GrowthRate.FAST, 25, false),
|
new PokemonSpecies(Species.IGGLYBUFF, 2, false, false, false, "Balloon Pokémon", PokemonType.NORMAL, PokemonType.FAIRY, 0.3, 1, Abilities.CUTE_CHARM, Abilities.COMPETITIVE, Abilities.FRIEND_GUARD, 210, 90, 30, 15, 40, 20, 15, 170, 50, 42, GrowthRate.FAST, 25, false),
|
||||||
@ -3121,7 +3121,7 @@ export function initSpecies() {
|
|||||||
),
|
),
|
||||||
new PokemonSpecies(Species.WALKING_WAKE, 9, false, false, false, "Paradox Pokémon", PokemonType.WATER, PokemonType.DRAGON, 3.5, 280, Abilities.PROTOSYNTHESIS, Abilities.NONE, Abilities.NONE, 590, 99, 83, 91, 125, 83, 109, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Gouging Fire and Raging Bolt
|
new PokemonSpecies(Species.WALKING_WAKE, 9, false, false, false, "Paradox Pokémon", PokemonType.WATER, PokemonType.DRAGON, 3.5, 280, Abilities.PROTOSYNTHESIS, Abilities.NONE, Abilities.NONE, 590, 99, 83, 91, 125, 83, 109, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Gouging Fire and Raging Bolt
|
||||||
new PokemonSpecies(Species.IRON_LEAVES, 9, false, false, false, "Paradox Pokémon", PokemonType.GRASS, PokemonType.PSYCHIC, 1.5, 125, Abilities.QUARK_DRIVE, Abilities.NONE, Abilities.NONE, 590, 90, 130, 88, 70, 108, 104, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Iron Boulder and Iron Crown
|
new PokemonSpecies(Species.IRON_LEAVES, 9, false, false, false, "Paradox Pokémon", PokemonType.GRASS, PokemonType.PSYCHIC, 1.5, 125, Abilities.QUARK_DRIVE, Abilities.NONE, Abilities.NONE, 590, 90, 130, 88, 70, 108, 104, 10, 0, 295, GrowthRate.SLOW, null, false), //Custom Catchrate, matching Iron Boulder and Iron Crown
|
||||||
new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
|
new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 4.4, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
|
||||||
new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false,
|
new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false,
|
||||||
new PokemonForm("Counterfeit Form", "counterfeit", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
|
new PokemonForm("Counterfeit Form", "counterfeit", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
|
||||||
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, false, true),
|
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, false, true),
|
||||||
|
@ -224,16 +224,16 @@ export const trainerPartyTemplates = {
|
|||||||
*/
|
*/
|
||||||
export function getEvilGruntPartyTemplate(): TrainerPartyTemplate {
|
export function getEvilGruntPartyTemplate(): TrainerPartyTemplate {
|
||||||
const waveIndex = globalScene.currentBattle?.waveIndex;
|
const waveIndex = globalScene.currentBattle?.waveIndex;
|
||||||
if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_1){
|
if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_1) {
|
||||||
return trainerPartyTemplates.TWO_AVG;
|
return trainerPartyTemplates.TWO_AVG;
|
||||||
}
|
}
|
||||||
if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_2){
|
if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_2) {
|
||||||
return trainerPartyTemplates.THREE_AVG;
|
return trainerPartyTemplates.THREE_AVG;
|
||||||
}
|
}
|
||||||
if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_3){
|
if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_3) {
|
||||||
return trainerPartyTemplates.TWO_AVG_ONE_STRONG;
|
return trainerPartyTemplates.TWO_AVG_ONE_STRONG;
|
||||||
}
|
}
|
||||||
if (waveIndex <= ClassicFixedBossWaves.EVIL_ADMIN_1){
|
if (waveIndex <= ClassicFixedBossWaves.EVIL_ADMIN_1) {
|
||||||
return trainerPartyTemplates.GYM_LEADER_4; // 3avg 1 strong 1 stronger
|
return trainerPartyTemplates.GYM_LEADER_4; // 3avg 1 strong 1 stronger
|
||||||
}
|
}
|
||||||
return trainerPartyTemplates.GYM_LEADER_5; // 3 avg 2 strong 1 stronger
|
return trainerPartyTemplates.GYM_LEADER_5; // 3 avg 2 strong 1 stronger
|
||||||
@ -251,7 +251,7 @@ export function getGymLeaderPartyTemplate() {
|
|||||||
switch (gameMode.modeId) {
|
switch (gameMode.modeId) {
|
||||||
case GameModes.DAILY:
|
case GameModes.DAILY:
|
||||||
if (currentBattle?.waveIndex <= 20) {
|
if (currentBattle?.waveIndex <= 20) {
|
||||||
return trainerPartyTemplates.GYM_LEADER_2
|
return trainerPartyTemplates.GYM_LEADER_2;
|
||||||
}
|
}
|
||||||
return trainerPartyTemplates.GYM_LEADER_3;
|
return trainerPartyTemplates.GYM_LEADER_3;
|
||||||
case GameModes.CHALLENGE: // In the future, there may be a ChallengeType to call here. For now, use classic's.
|
case GameModes.CHALLENGE: // In the future, there may be a ChallengeType to call here. For now, use classic's.
|
||||||
@ -259,13 +259,15 @@ export function getGymLeaderPartyTemplate() {
|
|||||||
if (currentBattle?.waveIndex <= 20) {
|
if (currentBattle?.waveIndex <= 20) {
|
||||||
return trainerPartyTemplates.GYM_LEADER_1; // 1 avg 1 strong
|
return trainerPartyTemplates.GYM_LEADER_1; // 1 avg 1 strong
|
||||||
}
|
}
|
||||||
else if (currentBattle?.waveIndex <= 30) {
|
if (currentBattle?.waveIndex <= 30) {
|
||||||
return trainerPartyTemplates.GYM_LEADER_2; // 1 avg 1 strong 1 stronger
|
return trainerPartyTemplates.GYM_LEADER_2; // 1 avg 1 strong 1 stronger
|
||||||
}
|
}
|
||||||
else if (currentBattle?.waveIndex <= 60) { // 50 and 60
|
// 50 and 60
|
||||||
|
if (currentBattle?.waveIndex <= 60) {
|
||||||
return trainerPartyTemplates.GYM_LEADER_3; // 2 avg 1 strong 1 stronger
|
return trainerPartyTemplates.GYM_LEADER_3; // 2 avg 1 strong 1 stronger
|
||||||
}
|
}
|
||||||
else if (currentBattle?.waveIndex <= 90) { // 80 and 90
|
// 80 and 90
|
||||||
|
if (currentBattle?.waveIndex <= 90) {
|
||||||
return trainerPartyTemplates.GYM_LEADER_4; // 3 avg 1 strong 1 stronger
|
return trainerPartyTemplates.GYM_LEADER_4; // 3 avg 1 strong 1 stronger
|
||||||
}
|
}
|
||||||
// 110+
|
// 110+
|
||||||
|
2367
src/field/pokemon.ts
@ -29,7 +29,7 @@ window.addEventListener("unhandledrejection", event => {
|
|||||||
const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) {
|
const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) {
|
||||||
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
||||||
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
||||||
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
return this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
||||||
};
|
};
|
||||||
|
|
||||||
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
||||||
|
@ -24,7 +24,7 @@ export class RevivalBlessingPhase extends BattlePhase {
|
|||||||
UiMode.PARTY,
|
UiMode.PARTY,
|
||||||
PartyUiMode.REVIVAL_BLESSING,
|
PartyUiMode.REVIVAL_BLESSING,
|
||||||
this.user.getFieldIndex(),
|
this.user.getFieldIndex(),
|
||||||
(slotIndex: integer, _option: PartyOption) => {
|
(slotIndex: number, _option: PartyOption) => {
|
||||||
if (slotIndex >= 0 && slotIndex < 6) {
|
if (slotIndex >= 0 && slotIndex < 6) {
|
||||||
const pokemon = globalScene.getPlayerParty()[slotIndex];
|
const pokemon = globalScene.getPlayerParty()[slotIndex];
|
||||||
if (!pokemon || !pokemon.isFainted()) {
|
if (!pokemon || !pokemon.isFainted()) {
|
||||||
|
@ -125,6 +125,12 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
const switchedInPokemon: Pokemon | undefined = party[this.slotIndex];
|
const switchedInPokemon: Pokemon | undefined = party[this.slotIndex];
|
||||||
this.lastPokemon = this.getPokemon();
|
this.lastPokemon = this.getPokemon();
|
||||||
|
|
||||||
|
// Defensive programming: Overcome the bug where the summon data has somehow not been reset
|
||||||
|
// prior to switching in a new Pokemon.
|
||||||
|
// Force the switch to occur and load the assets for the new pokemon, ignoring override.
|
||||||
|
switchedInPokemon.resetSummonData();
|
||||||
|
switchedInPokemon.loadAssets(true);
|
||||||
|
|
||||||
applyPreSummonAbAttrs(PreSummonAbAttr, switchedInPokemon);
|
applyPreSummonAbAttrs(PreSummonAbAttr, switchedInPokemon);
|
||||||
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
|
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
|
||||||
if (!switchedInPokemon) {
|
if (!switchedInPokemon) {
|
||||||
@ -132,6 +138,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.switchType === SwitchType.BATON_PASS) {
|
if (this.switchType === SwitchType.BATON_PASS) {
|
||||||
// If switching via baton pass, update opposing tags coming from the prior pokemon
|
// If switching via baton pass, update opposing tags coming from the prior pokemon
|
||||||
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) =>
|
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) =>
|
||||||
|
12
src/typings/phaser/index.d.ts
vendored
@ -20,37 +20,37 @@ declare module "phaser" {
|
|||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
setPositionRelative(guideObject: any, x: number, y: number): void;
|
setPositionRelative(guideObject: any, x: number, y: number): this;
|
||||||
}
|
}
|
||||||
interface Sprite {
|
interface Sprite {
|
||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
setPositionRelative(guideObject: any, x: number, y: number): void;
|
setPositionRelative(guideObject: any, x: number, y: number): this;
|
||||||
}
|
}
|
||||||
interface Image {
|
interface Image {
|
||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
setPositionRelative(guideObject: any, x: number, y: number): void;
|
setPositionRelative(guideObject: any, x: number, y: number): this;
|
||||||
}
|
}
|
||||||
interface NineSlice {
|
interface NineSlice {
|
||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
setPositionRelative(guideObject: any, x: number, y: number): void;
|
setPositionRelative(guideObject: any, x: number, y: number): this;
|
||||||
}
|
}
|
||||||
interface Text {
|
interface Text {
|
||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
setPositionRelative(guideObject: any, x: number, y: number): void;
|
setPositionRelative(guideObject: any, x: number, y: number): this;
|
||||||
}
|
}
|
||||||
interface Rectangle {
|
interface Rectangle {
|
||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
setPositionRelative(guideObject: any, x: number, y: number): void;
|
setPositionRelative(guideObject: any, x: number, y: number): this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ export class UiInputs {
|
|||||||
|
|
||||||
buttonInfo(pressed = true): void {
|
buttonInfo(pressed = true): void {
|
||||||
if (globalScene.showMovesetFlyout) {
|
if (globalScene.showMovesetFlyout) {
|
||||||
for (const p of globalScene.getField().filter(p => p?.isActive(true))) {
|
for (const p of globalScene.getEnemyField().filter(p => p?.isActive(true))) {
|
||||||
p.toggleFlyout(pressed);
|
p.toggleFlyout(pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { default as Pokemon } from "../field/pokemon";
|
import type { EnemyPokemon, default as Pokemon } from "../field/pokemon";
|
||||||
import { addTextObject, TextStyle } from "./text";
|
import { addTextObject, TextStyle } from "./text";
|
||||||
import { fixedInt } from "#app/utils/common";
|
import { fixedInt } from "#app/utils/common";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -126,7 +126,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
|
|||||||
* Links the given {@linkcode Pokemon} and subscribes to the {@linkcode BattleSceneEventType.MOVE_USED} event
|
* Links the given {@linkcode Pokemon} and subscribes to the {@linkcode BattleSceneEventType.MOVE_USED} event
|
||||||
* @param pokemon {@linkcode Pokemon} to link to this flyout
|
* @param pokemon {@linkcode Pokemon} to link to this flyout
|
||||||
*/
|
*/
|
||||||
initInfo(pokemon: Pokemon) {
|
initInfo(pokemon: EnemyPokemon) {
|
||||||
this.pokemon = pokemon;
|
this.pokemon = pokemon;
|
||||||
|
|
||||||
this.name = `Flyout ${getPokemonNameWithAffix(this.pokemon)}`;
|
this.name = `Flyout ${getPokemonNameWithAffix(this.pokemon)}`;
|
||||||
|
@ -1,986 +0,0 @@
|
|||||||
import type { EnemyPokemon, default as Pokemon } from "../field/pokemon";
|
|
||||||
import { getLevelTotalExp, getLevelRelExp } from "../data/exp";
|
|
||||||
import { getLocalizedSpriteKey, fixedInt } from "#app/utils/common";
|
|
||||||
import { addTextObject, TextStyle } from "./text";
|
|
||||||
import { getGenderSymbol, getGenderColor, Gender } from "../data/gender";
|
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
|
||||||
import { globalScene } from "#app/global-scene";
|
|
||||||
import { getTypeRgb } from "#app/data/type";
|
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
|
||||||
import { getVariantTint } from "#app/sprites/variant";
|
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import BattleFlyout from "./battle-flyout";
|
|
||||||
import { WindowVariant, addWindow } from "./ui-theme";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
|
|
||||||
|
|
||||||
export default class BattleInfo extends Phaser.GameObjects.Container {
|
|
||||||
public static readonly EXP_GAINS_DURATION_BASE = 1650;
|
|
||||||
|
|
||||||
private baseY: number;
|
|
||||||
|
|
||||||
private player: boolean;
|
|
||||||
private mini: boolean;
|
|
||||||
private boss: boolean;
|
|
||||||
private bossSegments: number;
|
|
||||||
private offset: boolean;
|
|
||||||
private lastName: string | null;
|
|
||||||
private lastTeraType: PokemonType;
|
|
||||||
private lastStatus: StatusEffect;
|
|
||||||
private lastHp: number;
|
|
||||||
private lastMaxHp: number;
|
|
||||||
private lastHpFrame: string | null;
|
|
||||||
private lastExp: number;
|
|
||||||
private lastLevelExp: number;
|
|
||||||
private lastLevel: number;
|
|
||||||
private lastLevelCapped: boolean;
|
|
||||||
private lastStats: string;
|
|
||||||
|
|
||||||
private box: Phaser.GameObjects.Sprite;
|
|
||||||
private nameText: Phaser.GameObjects.Text;
|
|
||||||
private genderText: Phaser.GameObjects.Text;
|
|
||||||
private ownedIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private championRibbon: Phaser.GameObjects.Sprite;
|
|
||||||
private teraIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private shinyIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private fusionShinyIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private splicedIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private statusIndicator: Phaser.GameObjects.Sprite;
|
|
||||||
private levelContainer: Phaser.GameObjects.Container;
|
|
||||||
private hpBar: Phaser.GameObjects.Image;
|
|
||||||
private hpBarSegmentDividers: Phaser.GameObjects.Rectangle[];
|
|
||||||
private levelNumbersContainer: Phaser.GameObjects.Container;
|
|
||||||
private hpNumbersContainer: Phaser.GameObjects.Container;
|
|
||||||
private type1Icon: Phaser.GameObjects.Sprite;
|
|
||||||
private type2Icon: Phaser.GameObjects.Sprite;
|
|
||||||
private type3Icon: Phaser.GameObjects.Sprite;
|
|
||||||
private expBar: Phaser.GameObjects.Image;
|
|
||||||
|
|
||||||
// #region Type effectiveness hint objects
|
|
||||||
private effectivenessContainer: Phaser.GameObjects.Container;
|
|
||||||
private effectivenessWindow: Phaser.GameObjects.NineSlice;
|
|
||||||
private effectivenessText: Phaser.GameObjects.Text;
|
|
||||||
private currentEffectiveness?: string;
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
public expMaskRect: Phaser.GameObjects.Graphics;
|
|
||||||
|
|
||||||
private statsContainer: Phaser.GameObjects.Container;
|
|
||||||
private statsBox: Phaser.GameObjects.Sprite;
|
|
||||||
private statValuesContainer: Phaser.GameObjects.Container;
|
|
||||||
private statNumbers: Phaser.GameObjects.Sprite[];
|
|
||||||
|
|
||||||
public flyoutMenu?: BattleFlyout;
|
|
||||||
|
|
||||||
private statOrder: Stat[];
|
|
||||||
private readonly statOrderPlayer = [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
|
|
||||||
private readonly statOrderEnemy = [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
|
|
||||||
|
|
||||||
constructor(x: number, y: number, player: boolean) {
|
|
||||||
super(globalScene, x, y);
|
|
||||||
this.baseY = y;
|
|
||||||
this.player = player;
|
|
||||||
this.mini = !player;
|
|
||||||
this.boss = false;
|
|
||||||
this.offset = false;
|
|
||||||
this.lastName = null;
|
|
||||||
this.lastTeraType = PokemonType.UNKNOWN;
|
|
||||||
this.lastStatus = StatusEffect.NONE;
|
|
||||||
this.lastHp = -1;
|
|
||||||
this.lastMaxHp = -1;
|
|
||||||
this.lastHpFrame = null;
|
|
||||||
this.lastExp = -1;
|
|
||||||
this.lastLevelExp = -1;
|
|
||||||
this.lastLevel = -1;
|
|
||||||
|
|
||||||
// Initially invisible and shown via Pokemon.showInfo
|
|
||||||
this.setVisible(false);
|
|
||||||
|
|
||||||
this.box = globalScene.add.sprite(0, 0, this.getTextureName());
|
|
||||||
this.box.setName("box");
|
|
||||||
this.box.setOrigin(1, 0.5);
|
|
||||||
this.add(this.box);
|
|
||||||
|
|
||||||
this.nameText = addTextObject(player ? -115 : -124, player ? -15.2 : -11.2, "", TextStyle.BATTLE_INFO);
|
|
||||||
this.nameText.setName("text_name");
|
|
||||||
this.nameText.setOrigin(0, 0);
|
|
||||||
this.add(this.nameText);
|
|
||||||
|
|
||||||
this.genderText = addTextObject(0, 0, "", TextStyle.BATTLE_INFO);
|
|
||||||
this.genderText.setName("text_gender");
|
|
||||||
this.genderText.setOrigin(0, 0);
|
|
||||||
this.genderText.setPositionRelative(this.nameText, 0, 2);
|
|
||||||
this.add(this.genderText);
|
|
||||||
|
|
||||||
if (!this.player) {
|
|
||||||
this.ownedIcon = globalScene.add.sprite(0, 0, "icon_owned");
|
|
||||||
this.ownedIcon.setName("icon_owned");
|
|
||||||
this.ownedIcon.setVisible(false);
|
|
||||||
this.ownedIcon.setOrigin(0, 0);
|
|
||||||
this.ownedIcon.setPositionRelative(this.nameText, 0, 11.75);
|
|
||||||
this.add(this.ownedIcon);
|
|
||||||
|
|
||||||
this.championRibbon = globalScene.add.sprite(0, 0, "champion_ribbon");
|
|
||||||
this.championRibbon.setName("icon_champion_ribbon");
|
|
||||||
this.championRibbon.setVisible(false);
|
|
||||||
this.championRibbon.setOrigin(0, 0);
|
|
||||||
this.championRibbon.setPositionRelative(this.nameText, 8, 11.75);
|
|
||||||
this.add(this.championRibbon);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.teraIcon = globalScene.add.sprite(0, 0, "icon_tera");
|
|
||||||
this.teraIcon.setName("icon_tera");
|
|
||||||
this.teraIcon.setVisible(false);
|
|
||||||
this.teraIcon.setOrigin(0, 0);
|
|
||||||
this.teraIcon.setScale(0.5);
|
|
||||||
this.teraIcon.setPositionRelative(this.nameText, 0, 2);
|
|
||||||
this.teraIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
|
|
||||||
this.add(this.teraIcon);
|
|
||||||
|
|
||||||
this.shinyIcon = globalScene.add.sprite(0, 0, "shiny_star");
|
|
||||||
this.shinyIcon.setName("icon_shiny");
|
|
||||||
this.shinyIcon.setVisible(false);
|
|
||||||
this.shinyIcon.setOrigin(0, 0);
|
|
||||||
this.shinyIcon.setScale(0.5);
|
|
||||||
this.shinyIcon.setPositionRelative(this.nameText, 0, 2);
|
|
||||||
this.shinyIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
|
|
||||||
this.add(this.shinyIcon);
|
|
||||||
|
|
||||||
this.fusionShinyIcon = globalScene.add.sprite(0, 0, "shiny_star_2");
|
|
||||||
this.fusionShinyIcon.setName("icon_fusion_shiny");
|
|
||||||
this.fusionShinyIcon.setVisible(false);
|
|
||||||
this.fusionShinyIcon.setOrigin(0, 0);
|
|
||||||
this.fusionShinyIcon.setScale(0.5);
|
|
||||||
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
|
|
||||||
this.add(this.fusionShinyIcon);
|
|
||||||
|
|
||||||
this.splicedIcon = globalScene.add.sprite(0, 0, "icon_spliced");
|
|
||||||
this.splicedIcon.setName("icon_spliced");
|
|
||||||
this.splicedIcon.setVisible(false);
|
|
||||||
this.splicedIcon.setOrigin(0, 0);
|
|
||||||
this.splicedIcon.setScale(0.5);
|
|
||||||
this.splicedIcon.setPositionRelative(this.nameText, 0, 2);
|
|
||||||
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
|
|
||||||
this.add(this.splicedIcon);
|
|
||||||
|
|
||||||
this.statusIndicator = globalScene.add.sprite(0, 0, getLocalizedSpriteKey("statuses"));
|
|
||||||
this.statusIndicator.setName("icon_status");
|
|
||||||
this.statusIndicator.setVisible(false);
|
|
||||||
this.statusIndicator.setOrigin(0, 0);
|
|
||||||
this.statusIndicator.setPositionRelative(this.nameText, 0, 11.5);
|
|
||||||
this.add(this.statusIndicator);
|
|
||||||
|
|
||||||
this.levelContainer = globalScene.add.container(player ? -41 : -50, player ? -10 : -5);
|
|
||||||
this.levelContainer.setName("container_level");
|
|
||||||
this.add(this.levelContainer);
|
|
||||||
|
|
||||||
const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
|
|
||||||
this.levelContainer.add(levelOverlay);
|
|
||||||
|
|
||||||
this.hpBar = globalScene.add.image(player ? -61 : -71, player ? -1 : 4.5, "overlay_hp");
|
|
||||||
this.hpBar.setName("hp_bar");
|
|
||||||
this.hpBar.setOrigin(0);
|
|
||||||
this.add(this.hpBar);
|
|
||||||
|
|
||||||
this.hpBarSegmentDividers = [];
|
|
||||||
|
|
||||||
this.levelNumbersContainer = globalScene.add.container(9.5, globalScene.uiTheme ? 0 : -0.5);
|
|
||||||
this.levelNumbersContainer.setName("container_level");
|
|
||||||
this.levelContainer.add(this.levelNumbersContainer);
|
|
||||||
|
|
||||||
if (this.player) {
|
|
||||||
this.hpNumbersContainer = globalScene.add.container(-15, 10);
|
|
||||||
this.hpNumbersContainer.setName("container_hp");
|
|
||||||
this.add(this.hpNumbersContainer);
|
|
||||||
|
|
||||||
const expBar = globalScene.add.image(-98, 18, "overlay_exp");
|
|
||||||
expBar.setName("overlay_exp");
|
|
||||||
expBar.setOrigin(0);
|
|
||||||
this.add(expBar);
|
|
||||||
|
|
||||||
const expMaskRect = globalScene.make.graphics({});
|
|
||||||
expMaskRect.setScale(6);
|
|
||||||
expMaskRect.fillStyle(0xffffff);
|
|
||||||
expMaskRect.beginPath();
|
|
||||||
expMaskRect.fillRect(127, 126, 85, 2);
|
|
||||||
|
|
||||||
const expMask = expMaskRect.createGeometryMask();
|
|
||||||
|
|
||||||
expBar.setMask(expMask);
|
|
||||||
|
|
||||||
this.expBar = expBar;
|
|
||||||
this.expMaskRect = expMaskRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.statsContainer = globalScene.add.container(0, 0);
|
|
||||||
this.statsContainer.setName("container_stats");
|
|
||||||
this.statsContainer.setAlpha(0);
|
|
||||||
this.add(this.statsContainer);
|
|
||||||
|
|
||||||
this.statsBox = globalScene.add.sprite(0, 0, `${this.getTextureName()}_stats`);
|
|
||||||
this.statsBox.setName("box_stats");
|
|
||||||
this.statsBox.setOrigin(1, 0.5);
|
|
||||||
this.statsContainer.add(this.statsBox);
|
|
||||||
|
|
||||||
const statLabels: Phaser.GameObjects.Sprite[] = [];
|
|
||||||
this.statNumbers = [];
|
|
||||||
|
|
||||||
this.statValuesContainer = globalScene.add.container(0, 0);
|
|
||||||
this.statsContainer.add(this.statValuesContainer);
|
|
||||||
|
|
||||||
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
|
|
||||||
// since the player won't have HP to show, it doesn't need to change from the current version
|
|
||||||
const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5;
|
|
||||||
const paddingX = this.player ? 4 : 2;
|
|
||||||
const statOverflow = this.player ? 1 : 0;
|
|
||||||
this.statOrder = this.player ? this.statOrderPlayer : this.statOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order
|
|
||||||
|
|
||||||
this.statOrder.map((s, i) => {
|
|
||||||
// we do a check for i > statOverflow to see when the stat labels go onto the next column
|
|
||||||
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
|
|
||||||
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
|
|
||||||
const statX =
|
|
||||||
i > statOverflow
|
|
||||||
? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX
|
|
||||||
: startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
|
|
||||||
|
|
||||||
const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
|
|
||||||
let statY: number; // this will be the y-axis placement for the labels
|
|
||||||
if (this.statOrder[i] === Stat.SPD || this.statOrder[i] === Stat.HP) {
|
|
||||||
statY = baseY + 5;
|
|
||||||
} else {
|
|
||||||
statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
|
|
||||||
}
|
|
||||||
|
|
||||||
const statLabel = globalScene.add.sprite(statX, statY, "pbinfo_stat", Stat[s]);
|
|
||||||
statLabel.setName("icon_stat_label_" + i.toString());
|
|
||||||
statLabel.setOrigin(0, 0);
|
|
||||||
statLabels.push(statLabel);
|
|
||||||
this.statValuesContainer.add(statLabel);
|
|
||||||
|
|
||||||
const statNumber = globalScene.add.sprite(
|
|
||||||
statX + statLabel.width,
|
|
||||||
statY,
|
|
||||||
"pbinfo_stat_numbers",
|
|
||||||
this.statOrder[i] !== Stat.HP ? "3" : "empty",
|
|
||||||
);
|
|
||||||
statNumber.setName("icon_stat_number_" + i.toString());
|
|
||||||
statNumber.setOrigin(0, 0);
|
|
||||||
this.statNumbers.push(statNumber);
|
|
||||||
this.statValuesContainer.add(statNumber);
|
|
||||||
|
|
||||||
if (this.statOrder[i] === Stat.HP) {
|
|
||||||
statLabel.setVisible(false);
|
|
||||||
statNumber.setVisible(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.player) {
|
|
||||||
this.flyoutMenu = new BattleFlyout(this.player);
|
|
||||||
this.add(this.flyoutMenu);
|
|
||||||
|
|
||||||
this.moveBelow<Phaser.GameObjects.GameObject>(this.flyoutMenu, this.box);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type1Icon = globalScene.add.sprite(
|
|
||||||
player ? -139 : -15,
|
|
||||||
player ? -17 : -15.5,
|
|
||||||
`pbinfo_${player ? "player" : "enemy"}_type1`,
|
|
||||||
);
|
|
||||||
this.type1Icon.setName("icon_type_1");
|
|
||||||
this.type1Icon.setOrigin(0, 0);
|
|
||||||
this.add(this.type1Icon);
|
|
||||||
|
|
||||||
this.type2Icon = globalScene.add.sprite(
|
|
||||||
player ? -139 : -15,
|
|
||||||
player ? -1 : -2.5,
|
|
||||||
`pbinfo_${player ? "player" : "enemy"}_type2`,
|
|
||||||
);
|
|
||||||
this.type2Icon.setName("icon_type_2");
|
|
||||||
this.type2Icon.setOrigin(0, 0);
|
|
||||||
this.add(this.type2Icon);
|
|
||||||
|
|
||||||
this.type3Icon = globalScene.add.sprite(
|
|
||||||
player ? -154 : 0,
|
|
||||||
player ? -17 : -15.5,
|
|
||||||
`pbinfo_${player ? "player" : "enemy"}_type`,
|
|
||||||
);
|
|
||||||
this.type3Icon.setName("icon_type_3");
|
|
||||||
this.type3Icon.setOrigin(0, 0);
|
|
||||||
this.add(this.type3Icon);
|
|
||||||
|
|
||||||
if (!this.player) {
|
|
||||||
this.effectivenessContainer = globalScene.add.container(0, 0);
|
|
||||||
this.effectivenessContainer.setPositionRelative(this.type1Icon, 22, 4);
|
|
||||||
this.effectivenessContainer.setVisible(false);
|
|
||||||
this.add(this.effectivenessContainer);
|
|
||||||
|
|
||||||
this.effectivenessText = addTextObject(5, 4.5, "", TextStyle.BATTLE_INFO);
|
|
||||||
this.effectivenessWindow = addWindow(0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN);
|
|
||||||
|
|
||||||
this.effectivenessContainer.add(this.effectivenessWindow);
|
|
||||||
this.effectivenessContainer.add(this.effectivenessText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getStatsValueContainer(): Phaser.GameObjects.Container {
|
|
||||||
return this.statValuesContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
initInfo(pokemon: Pokemon) {
|
|
||||||
this.updateNameText(pokemon);
|
|
||||||
const nameTextWidth = this.nameText.displayWidth;
|
|
||||||
|
|
||||||
this.name = pokemon.getNameToRender();
|
|
||||||
this.box.name = pokemon.getNameToRender();
|
|
||||||
|
|
||||||
this.flyoutMenu?.initInfo(pokemon);
|
|
||||||
|
|
||||||
this.genderText.setText(getGenderSymbol(pokemon.gender));
|
|
||||||
this.genderText.setColor(getGenderColor(pokemon.gender));
|
|
||||||
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
|
|
||||||
|
|
||||||
this.lastTeraType = pokemon.getTeraType();
|
|
||||||
|
|
||||||
this.teraIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
|
|
||||||
this.teraIcon.setVisible(pokemon.isTerastallized);
|
|
||||||
this.teraIcon.on("pointerover", () => {
|
|
||||||
if (pokemon.isTerastallized) {
|
|
||||||
globalScene.ui.showTooltip(
|
|
||||||
"",
|
|
||||||
i18next.t("fightUiHandler:teraHover", {
|
|
||||||
type: i18next.t(`pokemonInfo:Type.${PokemonType[this.lastTeraType]}`),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.teraIcon.on("pointerout", () => globalScene.ui.hideTooltip());
|
|
||||||
|
|
||||||
const isFusion = pokemon.isFusion(true);
|
|
||||||
|
|
||||||
this.splicedIcon.setPositionRelative(
|
|
||||||
this.nameText,
|
|
||||||
nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
|
|
||||||
2.5,
|
|
||||||
);
|
|
||||||
this.splicedIcon.setVisible(isFusion);
|
|
||||||
if (this.splicedIcon.visible) {
|
|
||||||
this.splicedIcon.on("pointerover", () =>
|
|
||||||
globalScene.ui.showTooltip(
|
|
||||||
"",
|
|
||||||
`${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.splicedIcon.on("pointerout", () => globalScene.ui.hideTooltip());
|
|
||||||
}
|
|
||||||
|
|
||||||
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
|
|
||||||
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
|
|
||||||
|
|
||||||
this.shinyIcon.setPositionRelative(
|
|
||||||
this.nameText,
|
|
||||||
nameTextWidth +
|
|
||||||
this.genderText.displayWidth +
|
|
||||||
1 +
|
|
||||||
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
|
|
||||||
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
|
|
||||||
2.5,
|
|
||||||
);
|
|
||||||
this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`);
|
|
||||||
this.shinyIcon.setVisible(pokemon.isShiny());
|
|
||||||
this.shinyIcon.setTint(getVariantTint(baseVariant));
|
|
||||||
if (this.shinyIcon.visible) {
|
|
||||||
const shinyDescriptor =
|
|
||||||
doubleShiny || baseVariant
|
|
||||||
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
|
|
||||||
: "";
|
|
||||||
this.shinyIcon.on("pointerover", () =>
|
|
||||||
globalScene.ui.showTooltip(
|
|
||||||
"",
|
|
||||||
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.shinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
|
|
||||||
this.fusionShinyIcon.setVisible(doubleShiny);
|
|
||||||
if (isFusion) {
|
|
||||||
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.player) {
|
|
||||||
if (this.nameText.visible) {
|
|
||||||
this.nameText.on("pointerover", () =>
|
|
||||||
globalScene.ui.showTooltip(
|
|
||||||
"",
|
|
||||||
i18next.t("battleInfo:generation", {
|
|
||||||
generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.nameText.on("pointerout", () => globalScene.ui.hideTooltip());
|
|
||||||
}
|
|
||||||
|
|
||||||
const dexEntry = globalScene.gameData.dexData[pokemon.species.speciesId];
|
|
||||||
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
|
|
||||||
const opponentPokemonDexAttr = pokemon.getDexAttr();
|
|
||||||
if (globalScene.gameMode.isClassic) {
|
|
||||||
if (
|
|
||||||
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
|
|
||||||
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
|
|
||||||
) {
|
|
||||||
this.championRibbon.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Player owns all genders and forms of the Pokemon
|
|
||||||
const missingDexAttrs = (dexEntry.caughtAttr & opponentPokemonDexAttr) < opponentPokemonDexAttr;
|
|
||||||
|
|
||||||
const ownedAbilityAttrs = globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
|
|
||||||
|
|
||||||
// Check if the player owns ability for the root form
|
|
||||||
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
|
|
||||||
|
|
||||||
if (missingDexAttrs || !playerOwnsThisAbility) {
|
|
||||||
this.ownedIcon.setTint(0x808080);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.boss) {
|
|
||||||
this.updateBossSegmentDividers(pokemon as EnemyPokemon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hpBar.setScale(pokemon.getHpRatio(true), 1);
|
|
||||||
this.lastHpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
|
|
||||||
this.hpBar.setFrame(this.lastHpFrame);
|
|
||||||
if (this.player) {
|
|
||||||
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
|
|
||||||
}
|
|
||||||
this.lastHp = pokemon.hp;
|
|
||||||
this.lastMaxHp = pokemon.getMaxHp();
|
|
||||||
|
|
||||||
this.setLevel(pokemon.level);
|
|
||||||
this.lastLevel = pokemon.level;
|
|
||||||
|
|
||||||
this.shinyIcon.setVisible(pokemon.isShiny());
|
|
||||||
|
|
||||||
const types = pokemon.getTypes(true, false, undefined, true);
|
|
||||||
this.type1Icon.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`);
|
|
||||||
this.type1Icon.setFrame(PokemonType[types[0]].toLowerCase());
|
|
||||||
this.type2Icon.setVisible(types.length > 1);
|
|
||||||
this.type3Icon.setVisible(types.length > 2);
|
|
||||||
if (types.length > 1) {
|
|
||||||
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
|
|
||||||
}
|
|
||||||
if (types.length > 2) {
|
|
||||||
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.player) {
|
|
||||||
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
|
|
||||||
this.lastExp = pokemon.exp;
|
|
||||||
this.lastLevelExp = pokemon.levelExp;
|
|
||||||
|
|
||||||
this.statValuesContainer.setPosition(8, 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stats = this.statOrder.map(() => 0);
|
|
||||||
|
|
||||||
this.lastStats = stats.join("");
|
|
||||||
this.updateStats(stats);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTextureName(): string {
|
|
||||||
return `pbinfo_${this.player ? "player" : "enemy"}${!this.player && this.boss ? "_boss" : this.mini ? "_mini" : ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMini(mini: boolean): void {
|
|
||||||
if (this.mini === mini) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mini = mini;
|
|
||||||
|
|
||||||
this.box.setTexture(this.getTextureName());
|
|
||||||
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
|
|
||||||
|
|
||||||
if (this.player) {
|
|
||||||
this.y -= 12 * (mini ? 1 : -1);
|
|
||||||
this.baseY = this.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
const offsetElements = [
|
|
||||||
this.nameText,
|
|
||||||
this.genderText,
|
|
||||||
this.teraIcon,
|
|
||||||
this.splicedIcon,
|
|
||||||
this.shinyIcon,
|
|
||||||
this.statusIndicator,
|
|
||||||
this.levelContainer,
|
|
||||||
];
|
|
||||||
offsetElements.forEach(el => (el.y += 1.5 * (mini ? -1 : 1)));
|
|
||||||
|
|
||||||
[this.type1Icon, this.type2Icon, this.type3Icon].forEach(el => {
|
|
||||||
el.x += 4 * (mini ? 1 : -1);
|
|
||||||
el.y += -8 * (mini ? 1 : -1);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
|
|
||||||
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
|
|
||||||
|
|
||||||
const toggledElements = [this.hpNumbersContainer, this.expBar];
|
|
||||||
toggledElements.forEach(el => el.setVisible(!mini));
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleStats(visible: boolean): void {
|
|
||||||
globalScene.tweens.add({
|
|
||||||
targets: this.statsContainer,
|
|
||||||
duration: fixedInt(125),
|
|
||||||
ease: "Sine.easeInOut",
|
|
||||||
alpha: visible ? 1 : 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBossSegments(pokemon: EnemyPokemon): void {
|
|
||||||
const boss = !!pokemon.bossSegments;
|
|
||||||
|
|
||||||
if (boss !== this.boss) {
|
|
||||||
this.boss = boss;
|
|
||||||
|
|
||||||
[
|
|
||||||
this.nameText,
|
|
||||||
this.genderText,
|
|
||||||
this.teraIcon,
|
|
||||||
this.splicedIcon,
|
|
||||||
this.shinyIcon,
|
|
||||||
this.ownedIcon,
|
|
||||||
this.championRibbon,
|
|
||||||
this.statusIndicator,
|
|
||||||
this.statValuesContainer,
|
|
||||||
].map(e => (e.x += 48 * (boss ? -1 : 1)));
|
|
||||||
this.hpBar.x += 38 * (boss ? -1 : 1);
|
|
||||||
this.hpBar.y += 2 * (this.boss ? -1 : 1);
|
|
||||||
this.levelContainer.x += 2 * (boss ? -1 : 1);
|
|
||||||
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
|
|
||||||
this.box.setTexture(this.getTextureName());
|
|
||||||
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bossSegments = boss ? pokemon.bossSegments : 0;
|
|
||||||
this.updateBossSegmentDividers(pokemon);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
|
|
||||||
while (this.hpBarSegmentDividers.length) {
|
|
||||||
this.hpBarSegmentDividers.pop()?.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.boss && this.bossSegments > 1) {
|
|
||||||
const uiTheme = globalScene.uiTheme;
|
|
||||||
const maxHp = pokemon.getMaxHp();
|
|
||||||
for (let s = 1; s < this.bossSegments; s++) {
|
|
||||||
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
|
|
||||||
const divider = globalScene.add.rectangle(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
this.hpBar.height - (uiTheme ? 0 : 1),
|
|
||||||
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
|
|
||||||
);
|
|
||||||
divider.setOrigin(0.5, 0);
|
|
||||||
divider.setName("hpBar_divider_" + s.toString());
|
|
||||||
this.add(divider);
|
|
||||||
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
|
|
||||||
|
|
||||||
divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
|
|
||||||
this.hpBarSegmentDividers.push(divider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setOffset(offset: boolean): void {
|
|
||||||
if (this.offset === offset) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.offset = offset;
|
|
||||||
|
|
||||||
this.x += 10 * (this.offset === this.player ? 1 : -1);
|
|
||||||
this.y += 27 * (this.offset ? 1 : -1);
|
|
||||||
this.baseY = this.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateInfo(pokemon: Pokemon, instant?: boolean): Promise<void> {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
if (!globalScene) {
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
|
|
||||||
|
|
||||||
this.genderText.setText(getGenderSymbol(gender));
|
|
||||||
this.genderText.setColor(getGenderColor(gender));
|
|
||||||
|
|
||||||
const nameUpdated = this.lastName !== pokemon.getNameToRender();
|
|
||||||
|
|
||||||
if (nameUpdated) {
|
|
||||||
this.updateNameText(pokemon);
|
|
||||||
this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const teraType = pokemon.isTerastallized ? pokemon.getTeraType() : PokemonType.UNKNOWN;
|
|
||||||
const teraTypeUpdated = this.lastTeraType !== teraType;
|
|
||||||
|
|
||||||
if (teraTypeUpdated) {
|
|
||||||
this.teraIcon.setVisible(teraType !== PokemonType.UNKNOWN);
|
|
||||||
this.teraIcon.setPositionRelative(
|
|
||||||
this.nameText,
|
|
||||||
this.nameText.displayWidth + this.genderText.displayWidth + 1,
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
this.teraIcon.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(teraType)));
|
|
||||||
this.lastTeraType = teraType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFusion = pokemon.isFusion(true);
|
|
||||||
|
|
||||||
if (nameUpdated || teraTypeUpdated) {
|
|
||||||
this.splicedIcon.setVisible(isFusion);
|
|
||||||
|
|
||||||
this.teraIcon.setPositionRelative(
|
|
||||||
this.nameText,
|
|
||||||
this.nameText.displayWidth + this.genderText.displayWidth + 1,
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
this.splicedIcon.setPositionRelative(
|
|
||||||
this.nameText,
|
|
||||||
this.nameText.displayWidth +
|
|
||||||
this.genderText.displayWidth +
|
|
||||||
1 +
|
|
||||||
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
|
|
||||||
1.5,
|
|
||||||
);
|
|
||||||
this.shinyIcon.setPositionRelative(
|
|
||||||
this.nameText,
|
|
||||||
this.nameText.displayWidth +
|
|
||||||
this.genderText.displayWidth +
|
|
||||||
1 +
|
|
||||||
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
|
|
||||||
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
|
|
||||||
2.5,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
|
|
||||||
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;
|
|
||||||
|
|
||||||
if (this.lastStatus !== StatusEffect.NONE) {
|
|
||||||
this.statusIndicator.setFrame(StatusEffect[this.lastStatus].toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
const offsetX = !this.player ? (this.ownedIcon.visible ? 8 : 0) + (this.championRibbon.visible ? 8 : 0) : 0;
|
|
||||||
this.statusIndicator.setPositionRelative(this.nameText, offsetX, 11.5);
|
|
||||||
|
|
||||||
this.statusIndicator.setVisible(!!this.lastStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
const types = pokemon.getTypes(true, false, undefined, true);
|
|
||||||
this.type1Icon.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`);
|
|
||||||
this.type1Icon.setFrame(PokemonType[types[0]].toLowerCase());
|
|
||||||
this.type2Icon.setVisible(types.length > 1);
|
|
||||||
this.type3Icon.setVisible(types.length > 2);
|
|
||||||
if (types.length > 1) {
|
|
||||||
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
|
|
||||||
}
|
|
||||||
if (types.length > 2) {
|
|
||||||
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateHpFrame = () => {
|
|
||||||
const hpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
|
|
||||||
if (hpFrame !== this.lastHpFrame) {
|
|
||||||
this.hpBar.setFrame(hpFrame);
|
|
||||||
this.lastHpFrame = hpFrame;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatePokemonHp = () => {
|
|
||||||
let duration = !instant ? Phaser.Math.Clamp(Math.abs(this.lastHp - pokemon.hp) * 5, 250, 5000) : 0;
|
|
||||||
const speed = globalScene.hpBarSpeed;
|
|
||||||
if (speed) {
|
|
||||||
duration = speed >= 3 ? 0 : duration / Math.pow(2, speed);
|
|
||||||
}
|
|
||||||
globalScene.tweens.add({
|
|
||||||
targets: this.hpBar,
|
|
||||||
ease: "Sine.easeOut",
|
|
||||||
scaleX: pokemon.getHpRatio(true),
|
|
||||||
duration: duration,
|
|
||||||
onUpdate: () => {
|
|
||||||
if (this.player && this.lastHp !== pokemon.hp) {
|
|
||||||
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
|
|
||||||
this.setHpNumbers(tweenHp, pokemon.getMaxHp());
|
|
||||||
this.lastHp = tweenHp;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHpFrame();
|
|
||||||
},
|
|
||||||
onComplete: () => {
|
|
||||||
updateHpFrame();
|
|
||||||
// If, after tweening, the hp is different from the original (due to rounding), force the hp number display
|
|
||||||
// to update to the correct value.
|
|
||||||
if (this.player && this.lastHp !== pokemon.hp) {
|
|
||||||
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
|
|
||||||
this.lastHp = pokemon.hp;
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!this.player) {
|
|
||||||
this.lastHp = pokemon.hp;
|
|
||||||
}
|
|
||||||
this.lastMaxHp = pokemon.getMaxHp();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.player) {
|
|
||||||
const isLevelCapped = pokemon.level >= globalScene.getMaxExpLevel();
|
|
||||||
|
|
||||||
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
|
|
||||||
const originalResolve = resolve;
|
|
||||||
const durationMultipler = Math.max(
|
|
||||||
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(
|
|
||||||
1 - Math.min(pokemon.level - this.lastLevel, 10) / 10,
|
|
||||||
),
|
|
||||||
0.1,
|
|
||||||
);
|
|
||||||
resolve = () => this.updatePokemonExp(pokemon, false, durationMultipler).then(() => originalResolve());
|
|
||||||
} else if (isLevelCapped !== this.lastLevelCapped) {
|
|
||||||
this.setLevel(pokemon.level);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastLevelCapped = isLevelCapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
|
|
||||||
return updatePokemonHp();
|
|
||||||
}
|
|
||||||
if (!this.player && this.lastLevel !== pokemon.level) {
|
|
||||||
this.setLevel(pokemon.level);
|
|
||||||
this.lastLevel = pokemon.level;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stats = pokemon.getStatStages();
|
|
||||||
const statsStr = stats.join("");
|
|
||||||
|
|
||||||
if (this.lastStats !== statsStr) {
|
|
||||||
this.updateStats(stats);
|
|
||||||
this.lastStats = statsStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.shinyIcon.setVisible(pokemon.isShiny(true));
|
|
||||||
|
|
||||||
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
|
|
||||||
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
|
|
||||||
this.shinyIcon.setTint(getVariantTint(baseVariant));
|
|
||||||
|
|
||||||
this.fusionShinyIcon.setVisible(doubleShiny);
|
|
||||||
if (isFusion) {
|
|
||||||
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
|
|
||||||
}
|
|
||||||
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNameText(pokemon: Pokemon): void {
|
|
||||||
let displayName = pokemon.getNameToRender().replace(/[♂♀]/g, "");
|
|
||||||
let nameTextWidth: number;
|
|
||||||
|
|
||||||
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
|
|
||||||
nameTextWidth = nameSizeTest.displayWidth;
|
|
||||||
|
|
||||||
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
|
|
||||||
while (
|
|
||||||
nameTextWidth >
|
|
||||||
(this.player || !this.boss ? 60 : 98) -
|
|
||||||
((gender !== Gender.GENDERLESS ? 6 : 0) +
|
|
||||||
(pokemon.fusionSpecies ? 8 : 0) +
|
|
||||||
(pokemon.isShiny() ? 8 : 0) +
|
|
||||||
(Math.min(pokemon.level.toString().length, 3) - 3) * 8)
|
|
||||||
) {
|
|
||||||
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
|
|
||||||
nameSizeTest.setText(displayName);
|
|
||||||
nameTextWidth = nameSizeTest.displayWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
nameSizeTest.destroy();
|
|
||||||
|
|
||||||
this.nameText.setText(displayName);
|
|
||||||
this.lastName = pokemon.getNameToRender();
|
|
||||||
|
|
||||||
if (this.nameText.visible) {
|
|
||||||
this.nameText.setInteractive(
|
|
||||||
new Phaser.Geom.Rectangle(0, 0, this.nameText.width, this.nameText.height),
|
|
||||||
Phaser.Geom.Rectangle.Contains,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePokemonExp(pokemon: Pokemon, instant?: boolean, levelDurationMultiplier = 1): Promise<void> {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const levelUp = this.lastLevel < pokemon.level;
|
|
||||||
const relLevelExp = getLevelRelExp(this.lastLevel + 1, pokemon.species.growthRate);
|
|
||||||
const levelExp = levelUp ? relLevelExp : pokemon.levelExp;
|
|
||||||
let ratio = relLevelExp ? levelExp / relLevelExp : 0;
|
|
||||||
if (this.lastLevel >= globalScene.getMaxExpLevel(true)) {
|
|
||||||
if (levelUp) {
|
|
||||||
ratio = 1;
|
|
||||||
} else {
|
|
||||||
ratio = 0;
|
|
||||||
}
|
|
||||||
instant = true;
|
|
||||||
}
|
|
||||||
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(
|
|
||||||
1 - Math.max(this.lastLevel - 100, 0) / 150,
|
|
||||||
);
|
|
||||||
let duration =
|
|
||||||
this.visible && !instant
|
|
||||||
? ((levelExp - this.lastLevelExp) / relLevelExp) *
|
|
||||||
BattleInfo.EXP_GAINS_DURATION_BASE *
|
|
||||||
durationMultiplier *
|
|
||||||
levelDurationMultiplier
|
|
||||||
: 0;
|
|
||||||
const speed = globalScene.expGainsSpeed;
|
|
||||||
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
|
|
||||||
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
|
|
||||||
}
|
|
||||||
if (ratio === 1) {
|
|
||||||
this.lastLevelExp = 0;
|
|
||||||
this.lastLevel++;
|
|
||||||
} else {
|
|
||||||
this.lastExp = pokemon.exp;
|
|
||||||
this.lastLevelExp = pokemon.levelExp;
|
|
||||||
}
|
|
||||||
if (duration) {
|
|
||||||
globalScene.playSound("se/exp");
|
|
||||||
}
|
|
||||||
globalScene.tweens.add({
|
|
||||||
targets: this.expMaskRect,
|
|
||||||
ease: "Sine.easeIn",
|
|
||||||
x: ratio * 510,
|
|
||||||
duration: duration,
|
|
||||||
onComplete: () => {
|
|
||||||
if (!globalScene) {
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
if (duration) {
|
|
||||||
globalScene.sound.stopByKey("se/exp");
|
|
||||||
}
|
|
||||||
if (ratio === 1) {
|
|
||||||
globalScene.playSound("se/level_up");
|
|
||||||
this.setLevel(this.lastLevel);
|
|
||||||
globalScene.time.delayedCall(500 * levelDurationMultiplier, () => {
|
|
||||||
this.expMaskRect.x = 0;
|
|
||||||
this.updateInfo(pokemon, instant).then(() => resolve());
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setLevel(level: number): void {
|
|
||||||
const isCapped = level >= globalScene.getMaxExpLevel();
|
|
||||||
this.levelNumbersContainer.removeAll(true);
|
|
||||||
const levelStr = level.toString();
|
|
||||||
for (let i = 0; i < levelStr.length; i++) {
|
|
||||||
this.levelNumbersContainer.add(
|
|
||||||
globalScene.add.image(i * 8, 0, `numbers${isCapped && this.player ? "_red" : ""}`, levelStr[i]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.levelContainer.setX((this.player ? -41 : -50) - 8 * Math.max(levelStr.length - 3, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
setHpNumbers(hp: number, maxHp: number): void {
|
|
||||||
if (!this.player || !globalScene) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hpNumbersContainer.removeAll(true);
|
|
||||||
const hpStr = hp.toString();
|
|
||||||
const maxHpStr = maxHp.toString();
|
|
||||||
let offset = 0;
|
|
||||||
for (let i = maxHpStr.length - 1; i >= 0; i--) {
|
|
||||||
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", maxHpStr[i]));
|
|
||||||
}
|
|
||||||
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", "/"));
|
|
||||||
for (let i = hpStr.length - 1; i >= 0; i--) {
|
|
||||||
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", hpStr[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStats(stats: number[]): void {
|
|
||||||
this.statOrder.map((s, i) => {
|
|
||||||
if (s !== Stat.HP) {
|
|
||||||
this.statNumbers[i].setFrame(stats[s - 1].toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request the flyoutMenu to toggle if available and hides or shows the effectiveness window where necessary
|
|
||||||
*/
|
|
||||||
toggleFlyout(visible: boolean): void {
|
|
||||||
this.flyoutMenu?.toggleFlyout(visible);
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
this.effectivenessContainer?.setVisible(false);
|
|
||||||
} else {
|
|
||||||
this.updateEffectiveness(this.currentEffectiveness);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show or hide the type effectiveness multiplier window
|
|
||||||
* Passing undefined will hide the window
|
|
||||||
*/
|
|
||||||
updateEffectiveness(effectiveness?: string) {
|
|
||||||
if (this.player) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentEffectiveness = effectiveness;
|
|
||||||
|
|
||||||
if (!globalScene.typeHints || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) {
|
|
||||||
this.effectivenessContainer.setVisible(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.effectivenessText.setText(effectiveness);
|
|
||||||
this.effectivenessWindow.width = 10 + this.effectivenessText.displayWidth;
|
|
||||||
this.effectivenessContainer.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getBaseY(): number {
|
|
||||||
return this.baseY;
|
|
||||||
}
|
|
||||||
|
|
||||||
resetY(): void {
|
|
||||||
this.y = this.baseY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PlayerBattleInfo extends BattleInfo {
|
|
||||||
constructor() {
|
|
||||||
super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EnemyBattleInfo extends BattleInfo {
|
|
||||||
constructor() {
|
|
||||||
super(140, -141, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setMini(_mini: boolean): void {} // Always mini
|
|
||||||
}
|
|
688
src/ui/battle-info/battle-info.ts
Normal file
@ -0,0 +1,688 @@
|
|||||||
|
import type { default as Pokemon } from "../../field/pokemon";
|
||||||
|
import { getLocalizedSpriteKey, fixedInt, getShinyDescriptor } from "#app/utils/common";
|
||||||
|
import { addTextObject, TextStyle } from "../text";
|
||||||
|
import { getGenderSymbol, getGenderColor, Gender } from "../../data/gender";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { getTypeRgb } from "#app/data/type";
|
||||||
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
|
import { getVariantTint } from "#app/sprites/variant";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters influencing the position of elements within the battle info container
|
||||||
|
*/
|
||||||
|
export type BattleInfoParamList = {
|
||||||
|
/** X offset for the name text*/
|
||||||
|
nameTextX: number;
|
||||||
|
/** Y offset for the name text */
|
||||||
|
nameTextY: number;
|
||||||
|
/** X offset for the level container */
|
||||||
|
levelContainerX: number;
|
||||||
|
/** Y offset for the level container */
|
||||||
|
levelContainerY: number;
|
||||||
|
/** X offset for the hp bar */
|
||||||
|
hpBarX: number;
|
||||||
|
/** Y offset for the hp bar */
|
||||||
|
hpBarY: number;
|
||||||
|
/** Parameters for the stat box container */
|
||||||
|
statBox: {
|
||||||
|
/** The starting offset from the left of the label for the entries in the stat box */
|
||||||
|
xOffset: number;
|
||||||
|
/** The X padding between each number column */
|
||||||
|
paddingX: number;
|
||||||
|
/** The index of the stat entries at which paddingX is used instead of startingX */
|
||||||
|
statOverflow: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default abstract class BattleInfo extends Phaser.GameObjects.Container {
|
||||||
|
public static readonly EXP_GAINS_DURATION_BASE = 1650;
|
||||||
|
|
||||||
|
protected baseY: number;
|
||||||
|
protected baseLvContainerX: number;
|
||||||
|
|
||||||
|
protected player: boolean;
|
||||||
|
protected mini: boolean;
|
||||||
|
protected boss: boolean;
|
||||||
|
protected bossSegments: number;
|
||||||
|
protected offset: boolean;
|
||||||
|
protected lastName: string | null;
|
||||||
|
protected lastTeraType: PokemonType;
|
||||||
|
protected lastStatus: StatusEffect;
|
||||||
|
protected lastHp: number;
|
||||||
|
protected lastMaxHp: number;
|
||||||
|
protected lastHpFrame: string | null;
|
||||||
|
protected lastExp: number;
|
||||||
|
protected lastLevelExp: number;
|
||||||
|
protected lastLevel: number;
|
||||||
|
protected lastLevelCapped: boolean;
|
||||||
|
protected lastStats: string;
|
||||||
|
|
||||||
|
protected box: Phaser.GameObjects.Sprite;
|
||||||
|
protected nameText: Phaser.GameObjects.Text;
|
||||||
|
protected genderText: Phaser.GameObjects.Text;
|
||||||
|
protected teraIcon: Phaser.GameObjects.Sprite;
|
||||||
|
protected shinyIcon: Phaser.GameObjects.Sprite;
|
||||||
|
protected fusionShinyIcon: Phaser.GameObjects.Sprite;
|
||||||
|
protected splicedIcon: Phaser.GameObjects.Sprite;
|
||||||
|
protected statusIndicator: Phaser.GameObjects.Sprite;
|
||||||
|
protected levelContainer: Phaser.GameObjects.Container;
|
||||||
|
protected hpBar: Phaser.GameObjects.Image;
|
||||||
|
protected levelNumbersContainer: Phaser.GameObjects.Container;
|
||||||
|
protected type1Icon: Phaser.GameObjects.Sprite;
|
||||||
|
protected type2Icon: Phaser.GameObjects.Sprite;
|
||||||
|
protected type3Icon: Phaser.GameObjects.Sprite;
|
||||||
|
protected expBar: Phaser.GameObjects.Image;
|
||||||
|
|
||||||
|
public expMaskRect: Phaser.GameObjects.Graphics;
|
||||||
|
|
||||||
|
protected statsContainer: Phaser.GameObjects.Container;
|
||||||
|
protected statsBox: Phaser.GameObjects.Sprite;
|
||||||
|
protected statValuesContainer: Phaser.GameObjects.Container;
|
||||||
|
protected statNumbers: Phaser.GameObjects.Sprite[];
|
||||||
|
|
||||||
|
get statOrder(): Stat[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper method used by the constructor to create the tera and shiny icons next to the name */
|
||||||
|
private constructIcons() {
|
||||||
|
const hitArea = new Phaser.Geom.Rectangle(0, 0, 12, 15);
|
||||||
|
const hitCallback = Phaser.Geom.Rectangle.Contains;
|
||||||
|
|
||||||
|
this.teraIcon = globalScene.add
|
||||||
|
.sprite(0, 0, "icon_tera")
|
||||||
|
.setName("icon_tera")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0)
|
||||||
|
.setScale(0.5)
|
||||||
|
.setInteractive(hitArea, hitCallback)
|
||||||
|
.setPositionRelative(this.nameText, 0, 2);
|
||||||
|
|
||||||
|
this.shinyIcon = globalScene.add
|
||||||
|
.sprite(0, 0, "shiny_star")
|
||||||
|
.setName("icon_shiny")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0)
|
||||||
|
.setScale(0.5)
|
||||||
|
.setInteractive(hitArea, hitCallback)
|
||||||
|
.setPositionRelative(this.nameText, 0, 2);
|
||||||
|
|
||||||
|
this.fusionShinyIcon = globalScene.add
|
||||||
|
.sprite(0, 0, "shiny_star_2")
|
||||||
|
.setName("icon_fusion_shiny")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0)
|
||||||
|
.setScale(0.5)
|
||||||
|
.copyPosition(this.shinyIcon);
|
||||||
|
|
||||||
|
this.splicedIcon = globalScene.add
|
||||||
|
.sprite(0, 0, "icon_spliced")
|
||||||
|
.setName("icon_spliced")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0)
|
||||||
|
.setScale(0.5)
|
||||||
|
.setInteractive(hitArea, hitCallback)
|
||||||
|
.setPositionRelative(this.nameText, 0, 2);
|
||||||
|
|
||||||
|
this.add([this.teraIcon, this.shinyIcon, this.fusionShinyIcon, this.splicedIcon]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submethod of the constructor that creates and adds the stats container to the battle info
|
||||||
|
*/
|
||||||
|
protected constructStatContainer({ xOffset, paddingX, statOverflow }: BattleInfoParamList["statBox"]): void {
|
||||||
|
this.statsContainer = globalScene.add.container(0, 0).setName("container_stats").setAlpha(0);
|
||||||
|
this.add(this.statsContainer);
|
||||||
|
|
||||||
|
this.statsBox = globalScene.add
|
||||||
|
.sprite(0, 0, `${this.getTextureName()}_stats`)
|
||||||
|
.setName("box_stats")
|
||||||
|
.setOrigin(1, 0.5);
|
||||||
|
this.statsContainer.add(this.statsBox);
|
||||||
|
|
||||||
|
const statLabels: Phaser.GameObjects.Sprite[] = [];
|
||||||
|
this.statNumbers = [];
|
||||||
|
|
||||||
|
this.statValuesContainer = globalScene.add.container();
|
||||||
|
this.statsContainer.add(this.statValuesContainer);
|
||||||
|
|
||||||
|
const startingX = -this.statsBox.width + xOffset;
|
||||||
|
|
||||||
|
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
|
||||||
|
// since the player won't have HP to show, it doesn't need to change from the current version
|
||||||
|
|
||||||
|
for (const [i, s] of this.statOrder.entries()) {
|
||||||
|
const isHp = s === Stat.HP;
|
||||||
|
// we do a check for i > statOverflow to see when the stat labels go onto the next column
|
||||||
|
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
|
||||||
|
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
|
||||||
|
const statX =
|
||||||
|
i > statOverflow
|
||||||
|
? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX
|
||||||
|
: startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
|
||||||
|
|
||||||
|
let statY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
|
||||||
|
if (isHp || s === Stat.SPD) {
|
||||||
|
statY += 5;
|
||||||
|
} else if (this.player === !!(i % 2)) {
|
||||||
|
// we compare i % 2 against this.player to tell us where to place the label
|
||||||
|
// because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players
|
||||||
|
// this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
|
||||||
|
statY += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statLabel = globalScene.add
|
||||||
|
.sprite(statX, statY, "pbinfo_stat", Stat[s])
|
||||||
|
.setName("icon_stat_label_" + i.toString())
|
||||||
|
.setOrigin(0);
|
||||||
|
statLabels.push(statLabel);
|
||||||
|
this.statValuesContainer.add(statLabel);
|
||||||
|
|
||||||
|
const statNumber = globalScene.add
|
||||||
|
.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", !isHp ? "3" : "empty")
|
||||||
|
.setName("icon_stat_number_" + i.toString())
|
||||||
|
.setOrigin(0);
|
||||||
|
this.statNumbers.push(statNumber);
|
||||||
|
this.statValuesContainer.add(statNumber);
|
||||||
|
|
||||||
|
if (isHp) {
|
||||||
|
statLabel.setVisible(false);
|
||||||
|
statNumber.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submethod of the constructor that creates and adds the pokemon type icons to the battle info
|
||||||
|
*/
|
||||||
|
protected abstract constructTypeIcons(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x - The x position of the battle info container
|
||||||
|
* @param y - The y position of the battle info container
|
||||||
|
* @param player - Whether this battle info belongs to a player or an enemy
|
||||||
|
* @param posParams - The parameters influencing the position of elements within the battle info container
|
||||||
|
*/
|
||||||
|
constructor(x: number, y: number, player: boolean, posParams: BattleInfoParamList) {
|
||||||
|
super(globalScene, x, y);
|
||||||
|
this.baseY = y;
|
||||||
|
this.player = player;
|
||||||
|
this.mini = !player;
|
||||||
|
this.boss = false;
|
||||||
|
this.offset = false;
|
||||||
|
this.lastName = null;
|
||||||
|
this.lastTeraType = PokemonType.UNKNOWN;
|
||||||
|
this.lastStatus = StatusEffect.NONE;
|
||||||
|
this.lastHp = -1;
|
||||||
|
this.lastMaxHp = -1;
|
||||||
|
this.lastHpFrame = null;
|
||||||
|
this.lastExp = -1;
|
||||||
|
this.lastLevelExp = -1;
|
||||||
|
this.lastLevel = -1;
|
||||||
|
this.baseLvContainerX = posParams.levelContainerX;
|
||||||
|
|
||||||
|
// Initially invisible and shown via Pokemon.showInfo
|
||||||
|
this.setVisible(false);
|
||||||
|
|
||||||
|
this.box = globalScene.add.sprite(0, 0, this.getTextureName()).setName("box").setOrigin(1, 0.5);
|
||||||
|
this.add(this.box);
|
||||||
|
|
||||||
|
this.nameText = addTextObject(player ? -115 : -124, player ? -15.2 : -11.2, "", TextStyle.BATTLE_INFO)
|
||||||
|
.setName("text_name")
|
||||||
|
.setOrigin(0);
|
||||||
|
this.add(this.nameText);
|
||||||
|
|
||||||
|
this.genderText = addTextObject(0, 0, "", TextStyle.BATTLE_INFO)
|
||||||
|
.setName("text_gender")
|
||||||
|
.setOrigin(0)
|
||||||
|
.setPositionRelative(this.nameText, 0, 2);
|
||||||
|
this.add(this.genderText);
|
||||||
|
|
||||||
|
this.constructIcons();
|
||||||
|
|
||||||
|
this.statusIndicator = globalScene.add
|
||||||
|
.sprite(0, 0, getLocalizedSpriteKey("statuses"))
|
||||||
|
.setName("icon_status")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0)
|
||||||
|
.setPositionRelative(this.nameText, 0, 11.5);
|
||||||
|
this.add(this.statusIndicator);
|
||||||
|
|
||||||
|
this.levelContainer = globalScene.add
|
||||||
|
.container(posParams.levelContainerX, posParams.levelContainerY)
|
||||||
|
.setName("container_level");
|
||||||
|
this.add(this.levelContainer);
|
||||||
|
|
||||||
|
const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
|
||||||
|
this.levelContainer.add(levelOverlay);
|
||||||
|
|
||||||
|
this.hpBar = globalScene.add.image(posParams.hpBarX, posParams.hpBarY, "overlay_hp").setName("hp_bar").setOrigin(0);
|
||||||
|
this.add(this.hpBar);
|
||||||
|
|
||||||
|
this.levelNumbersContainer = globalScene.add
|
||||||
|
.container(9.5, globalScene.uiTheme ? 0 : -0.5)
|
||||||
|
.setName("container_level");
|
||||||
|
this.levelContainer.add(this.levelNumbersContainer);
|
||||||
|
|
||||||
|
this.constructStatContainer(posParams.statBox);
|
||||||
|
|
||||||
|
this.constructTypeIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatsValueContainer(): Phaser.GameObjects.Container {
|
||||||
|
return this.statValuesContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region Initialization methods
|
||||||
|
|
||||||
|
initSplicedIcon(pokemon: Pokemon, baseWidth: number) {
|
||||||
|
this.splicedIcon.setPositionRelative(
|
||||||
|
this.nameText,
|
||||||
|
baseWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
|
||||||
|
2.5,
|
||||||
|
);
|
||||||
|
this.splicedIcon.setVisible(pokemon.isFusion(true));
|
||||||
|
if (!this.splicedIcon.visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.splicedIcon
|
||||||
|
.on("pointerover", () =>
|
||||||
|
globalScene.ui.showTooltip(
|
||||||
|
"",
|
||||||
|
`${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on("pointerout", () => globalScene.ui.hideTooltip());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@linkcode initInfo} to initialize the shiny icon
|
||||||
|
* @param pokemon - The pokemon object attached to this battle info
|
||||||
|
* @param baseXOffset - The x offset to use for the shiny icon
|
||||||
|
* @param doubleShiny - Whether the pokemon is shiny and its fusion species is also shiny
|
||||||
|
*/
|
||||||
|
protected initShinyIcon(pokemon: Pokemon, xOffset: number, doubleShiny: boolean) {
|
||||||
|
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
|
||||||
|
|
||||||
|
this.shinyIcon.setPositionRelative(
|
||||||
|
this.nameText,
|
||||||
|
xOffset +
|
||||||
|
this.genderText.displayWidth +
|
||||||
|
1 +
|
||||||
|
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
|
||||||
|
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
|
||||||
|
2.5,
|
||||||
|
);
|
||||||
|
this.shinyIcon
|
||||||
|
.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`)
|
||||||
|
.setVisible(pokemon.isShiny())
|
||||||
|
.setTint(getVariantTint(baseVariant));
|
||||||
|
|
||||||
|
if (!this.shinyIcon.visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shinyDescriptor = "";
|
||||||
|
if (doubleShiny || baseVariant) {
|
||||||
|
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
|
||||||
|
if (doubleShiny) {
|
||||||
|
shinyDescriptor += "/" + getShinyDescriptor(pokemon.fusionVariant);
|
||||||
|
}
|
||||||
|
shinyDescriptor += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shinyIcon
|
||||||
|
.on("pointerover", () => globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor))
|
||||||
|
.on("pointerout", () => globalScene.ui.hideTooltip());
|
||||||
|
}
|
||||||
|
|
||||||
|
initInfo(pokemon: Pokemon) {
|
||||||
|
this.updateNameText(pokemon);
|
||||||
|
const nameTextWidth = this.nameText.displayWidth;
|
||||||
|
|
||||||
|
this.name = pokemon.getNameToRender();
|
||||||
|
this.box.name = pokemon.getNameToRender();
|
||||||
|
|
||||||
|
this.genderText
|
||||||
|
.setText(getGenderSymbol(pokemon.gender))
|
||||||
|
.setColor(getGenderColor(pokemon.gender))
|
||||||
|
.setPositionRelative(this.nameText, nameTextWidth, 0);
|
||||||
|
|
||||||
|
this.lastTeraType = pokemon.getTeraType();
|
||||||
|
|
||||||
|
this.teraIcon
|
||||||
|
.setVisible(pokemon.isTerastallized)
|
||||||
|
.on("pointerover", () => {
|
||||||
|
if (pokemon.isTerastallized) {
|
||||||
|
globalScene.ui.showTooltip(
|
||||||
|
"",
|
||||||
|
i18next.t("fightUiHandler:teraHover", {
|
||||||
|
type: i18next.t(`pokemonInfo:Type.${PokemonType[this.lastTeraType]}`),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on("pointerout", () => globalScene.ui.hideTooltip())
|
||||||
|
.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
|
||||||
|
|
||||||
|
const isFusion = pokemon.isFusion(true);
|
||||||
|
this.initSplicedIcon(pokemon, nameTextWidth);
|
||||||
|
|
||||||
|
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
|
||||||
|
this.initShinyIcon(pokemon, nameTextWidth, doubleShiny);
|
||||||
|
|
||||||
|
this.fusionShinyIcon.setVisible(doubleShiny).copyPosition(this.shinyIcon);
|
||||||
|
if (isFusion) {
|
||||||
|
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hpBar.setScale(pokemon.getHpRatio(true), 1);
|
||||||
|
this.lastHpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
|
||||||
|
this.hpBar.setFrame(this.lastHpFrame);
|
||||||
|
this.lastHp = pokemon.hp;
|
||||||
|
this.lastMaxHp = pokemon.getMaxHp();
|
||||||
|
|
||||||
|
this.setLevel(pokemon.level);
|
||||||
|
this.lastLevel = pokemon.level;
|
||||||
|
|
||||||
|
this.shinyIcon.setVisible(pokemon.isShiny());
|
||||||
|
|
||||||
|
this.setTypes(pokemon.getTypes(true, false, undefined, true));
|
||||||
|
|
||||||
|
const stats = this.statOrder.map(() => 0);
|
||||||
|
|
||||||
|
this.lastStats = stats.join("");
|
||||||
|
this.updateStats(stats);
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the texture name of the battle info box
|
||||||
|
*/
|
||||||
|
abstract getTextureName(): string;
|
||||||
|
|
||||||
|
setMini(_mini: boolean): void {}
|
||||||
|
|
||||||
|
toggleStats(visible: boolean): void {
|
||||||
|
globalScene.tweens.add({
|
||||||
|
targets: this.statsContainer,
|
||||||
|
duration: fixedInt(125),
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
alpha: visible ? 1 : 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setOffset(offset: boolean): void {
|
||||||
|
if (this.offset === offset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.offset = offset;
|
||||||
|
|
||||||
|
this.x += 10 * (this.offset === this.player ? 1 : -1);
|
||||||
|
this.y += 27 * (this.offset ? 1 : -1);
|
||||||
|
this.baseY = this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region Update methods and helpers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the status icon to match the pokemon's current status
|
||||||
|
* @param pokemon - The pokemon object attached to this battle info
|
||||||
|
* @param xOffset - The offset from the name text
|
||||||
|
*/
|
||||||
|
updateStatusIcon(pokemon: Pokemon, xOffset = 0) {
|
||||||
|
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
|
||||||
|
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;
|
||||||
|
|
||||||
|
if (this.lastStatus !== StatusEffect.NONE) {
|
||||||
|
this.statusIndicator.setFrame(StatusEffect[this.lastStatus].toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statusIndicator.setVisible(!!this.lastStatus).setPositionRelative(this.nameText, xOffset, 11.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update the pokemon name inside the container */
|
||||||
|
protected updateName(name: string): boolean {
|
||||||
|
if (this.lastName === name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.nameText.setText(name).setPositionRelative(this.box, -this.nameText.displayWidth, 0);
|
||||||
|
this.lastName = name;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updateTeraType(ty: PokemonType): boolean {
|
||||||
|
if (this.lastTeraType === ty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.teraIcon
|
||||||
|
.setVisible(ty !== PokemonType.UNKNOWN)
|
||||||
|
.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(ty)))
|
||||||
|
.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
|
||||||
|
this.lastTeraType = ty;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the type icons to match the pokemon's types
|
||||||
|
*/
|
||||||
|
setTypes(types: PokemonType[]): void {
|
||||||
|
this.type1Icon
|
||||||
|
.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`)
|
||||||
|
.setFrame(PokemonType[types[0]].toLowerCase());
|
||||||
|
this.type2Icon.setVisible(types.length > 1);
|
||||||
|
this.type3Icon.setVisible(types.length > 2);
|
||||||
|
if (types.length > 1) {
|
||||||
|
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
|
||||||
|
}
|
||||||
|
if (types.length > 2) {
|
||||||
|
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@linkcode updateInfo} to update the position of the tera, spliced, and shiny icons
|
||||||
|
* @param isFusion - Whether the pokemon is a fusion or not
|
||||||
|
*/
|
||||||
|
protected updateIconDisplay(isFusion: boolean): void {
|
||||||
|
this.teraIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
|
||||||
|
this.splicedIcon
|
||||||
|
.setVisible(isFusion)
|
||||||
|
.setPositionRelative(
|
||||||
|
this.nameText,
|
||||||
|
this.nameText.displayWidth +
|
||||||
|
this.genderText.displayWidth +
|
||||||
|
1 +
|
||||||
|
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
|
||||||
|
1.5,
|
||||||
|
);
|
||||||
|
this.shinyIcon.setPositionRelative(
|
||||||
|
this.nameText,
|
||||||
|
this.nameText.displayWidth +
|
||||||
|
this.genderText.displayWidth +
|
||||||
|
1 +
|
||||||
|
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
|
||||||
|
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
|
||||||
|
2.5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region Hp Bar Display handling
|
||||||
|
/**
|
||||||
|
* Called every time the hp frame is updated by the tween
|
||||||
|
* @param pokemon - The pokemon object attached to this battle info
|
||||||
|
*/
|
||||||
|
protected updateHpFrame(): void {
|
||||||
|
const hpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
|
||||||
|
if (hpFrame !== this.lastHpFrame) {
|
||||||
|
this.hpBar.setFrame(hpFrame);
|
||||||
|
this.lastHpFrame = hpFrame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by every frame in the hp animation tween created in {@linkcode updatePokemonHp}
|
||||||
|
* @param _pokemon - The pokemon the battle-info bar belongs to
|
||||||
|
*/
|
||||||
|
protected onHpTweenUpdate(_pokemon: Pokemon): void {
|
||||||
|
this.updateHpFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update the pokemonHp bar */
|
||||||
|
protected updatePokemonHp(pokemon: Pokemon, resolve: (r: void | PromiseLike<void>) => void, instant?: boolean): void {
|
||||||
|
let duration = !instant ? Phaser.Math.Clamp(Math.abs(this.lastHp - pokemon.hp) * 5, 250, 5000) : 0;
|
||||||
|
const speed = globalScene.hpBarSpeed;
|
||||||
|
if (speed) {
|
||||||
|
duration = speed >= 3 ? 0 : duration / Math.pow(2, speed);
|
||||||
|
}
|
||||||
|
globalScene.tweens.add({
|
||||||
|
targets: this.hpBar,
|
||||||
|
ease: "Sine.easeOut",
|
||||||
|
scaleX: pokemon.getHpRatio(true),
|
||||||
|
duration: duration,
|
||||||
|
onUpdate: () => {
|
||||||
|
this.onHpTweenUpdate(pokemon);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
this.updateHpFrame();
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.lastMaxHp = pokemon.getMaxHp();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
async updateInfo(pokemon: Pokemon, instant?: boolean): Promise<void> {
|
||||||
|
let resolve: (r: void | PromiseLike<void>) => void = () => {};
|
||||||
|
const promise = new Promise<void>(r => (resolve = r));
|
||||||
|
if (!globalScene) {
|
||||||
|
return resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const gender: Gender = pokemon.summonData?.illusion?.gender ?? pokemon.gender;
|
||||||
|
|
||||||
|
this.genderText.setText(getGenderSymbol(gender)).setColor(getGenderColor(gender));
|
||||||
|
|
||||||
|
const nameUpdated = this.updateName(pokemon.getNameToRender());
|
||||||
|
|
||||||
|
const teraTypeUpdated = this.updateTeraType(pokemon.isTerastallized ? pokemon.getTeraType() : PokemonType.UNKNOWN);
|
||||||
|
|
||||||
|
const isFusion = pokemon.isFusion(true);
|
||||||
|
|
||||||
|
if (nameUpdated || teraTypeUpdated) {
|
||||||
|
this.updateIconDisplay(isFusion);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateStatusIcon(pokemon);
|
||||||
|
|
||||||
|
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
|
||||||
|
return this.updatePokemonHp(pokemon, resolve, instant);
|
||||||
|
}
|
||||||
|
if (!this.player && this.lastLevel !== pokemon.level) {
|
||||||
|
this.setLevel(pokemon.level);
|
||||||
|
this.lastLevel = pokemon.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = pokemon.getStatStages();
|
||||||
|
const statsStr = stats.join("");
|
||||||
|
|
||||||
|
if (this.lastStats !== statsStr) {
|
||||||
|
this.updateStats(stats);
|
||||||
|
this.lastStats = statsStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shinyIcon.setVisible(pokemon.isShiny(true));
|
||||||
|
|
||||||
|
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
|
||||||
|
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
|
||||||
|
this.shinyIcon.setTint(getVariantTint(baseVariant));
|
||||||
|
|
||||||
|
this.fusionShinyIcon.setVisible(doubleShiny).setPosition(this.shinyIcon.x, this.shinyIcon.y);
|
||||||
|
if (isFusion) {
|
||||||
|
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
await promise;
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
updateNameText(pokemon: Pokemon): void {
|
||||||
|
let displayName = pokemon.getNameToRender().replace(/[♂♀]/g, "");
|
||||||
|
let nameTextWidth: number;
|
||||||
|
|
||||||
|
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
|
||||||
|
nameTextWidth = nameSizeTest.displayWidth;
|
||||||
|
|
||||||
|
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
|
||||||
|
while (
|
||||||
|
nameTextWidth >
|
||||||
|
(this.player || !this.boss ? 60 : 98) -
|
||||||
|
((gender !== Gender.GENDERLESS ? 6 : 0) +
|
||||||
|
(pokemon.fusionSpecies ? 8 : 0) +
|
||||||
|
(pokemon.isShiny() ? 8 : 0) +
|
||||||
|
(Math.min(pokemon.level.toString().length, 3) - 3) * 8)
|
||||||
|
) {
|
||||||
|
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
|
||||||
|
nameSizeTest.setText(displayName);
|
||||||
|
nameTextWidth = nameSizeTest.displayWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameSizeTest.destroy();
|
||||||
|
|
||||||
|
this.nameText.setText(displayName);
|
||||||
|
this.lastName = pokemon.getNameToRender();
|
||||||
|
|
||||||
|
if (this.nameText.visible) {
|
||||||
|
this.nameText.setInteractive(
|
||||||
|
new Phaser.Geom.Rectangle(0, 0, this.nameText.width, this.nameText.height),
|
||||||
|
Phaser.Geom.Rectangle.Contains,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the level numbers container to display the provided level
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* The numbers in the pokemon's level uses images for each number rather than a text object with a special font.
|
||||||
|
* This method sets the images for each digit of the level number and then positions the level container based
|
||||||
|
* on the number of digits.
|
||||||
|
*
|
||||||
|
* @param level - The level to display
|
||||||
|
* @param textureKey - The texture key for the level numbers
|
||||||
|
*/
|
||||||
|
setLevel(level: number, textureKey: "numbers" | "numbers_red" = "numbers"): void {
|
||||||
|
this.levelNumbersContainer.removeAll(true);
|
||||||
|
const levelStr = level.toString();
|
||||||
|
for (let i = 0; i < levelStr.length; i++) {
|
||||||
|
this.levelNumbersContainer.add(globalScene.add.image(i * 8, 0, textureKey, levelStr[i]));
|
||||||
|
}
|
||||||
|
this.levelContainer.setX(this.baseLvContainerX - 8 * Math.max(levelStr.length - 3, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStats(stats: number[]): void {
|
||||||
|
for (const [i, s] of this.statOrder.entries()) {
|
||||||
|
if (s !== Stat.HP) {
|
||||||
|
this.statNumbers[i].setFrame(stats[s - 1].toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseY(): number {
|
||||||
|
return this.baseY;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetY(): void {
|
||||||
|
this.y = this.baseY;
|
||||||
|
}
|
||||||
|
}
|
235
src/ui/battle-info/enemy-battle-info.ts
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import BattleFlyout from "../battle-flyout";
|
||||||
|
import { addTextObject, TextStyle } from "#app/ui/text";
|
||||||
|
import { addWindow, WindowVariant } from "#app/ui/ui-theme";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import type { GameObjects } from "phaser";
|
||||||
|
import BattleInfo from "./battle-info";
|
||||||
|
import type { BattleInfoParamList } from "./battle-info";
|
||||||
|
|
||||||
|
export class EnemyBattleInfo extends BattleInfo {
|
||||||
|
protected player: false = false;
|
||||||
|
protected championRibbon: Phaser.GameObjects.Sprite;
|
||||||
|
protected ownedIcon: Phaser.GameObjects.Sprite;
|
||||||
|
protected flyoutMenu: BattleFlyout;
|
||||||
|
|
||||||
|
protected hpBarSegmentDividers: GameObjects.Rectangle[] = [];
|
||||||
|
|
||||||
|
// #region Type effectiveness hint objects
|
||||||
|
protected effectivenessContainer: Phaser.GameObjects.Container;
|
||||||
|
protected effectivenessWindow: Phaser.GameObjects.NineSlice;
|
||||||
|
protected effectivenessText: Phaser.GameObjects.Text;
|
||||||
|
protected currentEffectiveness?: string;
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
override get statOrder(): Stat[] {
|
||||||
|
return [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
|
||||||
|
}
|
||||||
|
|
||||||
|
override getTextureName(): string {
|
||||||
|
return this.boss ? "pbinfo_enemy_boss_mini" : "pbinfo_enemy_mini";
|
||||||
|
}
|
||||||
|
|
||||||
|
override constructTypeIcons(): void {
|
||||||
|
this.type1Icon = globalScene.add.sprite(-15, -15.5, "pbinfo_enemy_type1").setName("icon_type_1").setOrigin(0);
|
||||||
|
this.type2Icon = globalScene.add.sprite(-15, -2.5, "pbinfo_enemy_type2").setName("icon_type_2").setOrigin(0);
|
||||||
|
this.type3Icon = globalScene.add.sprite(0, 15.5, "pbinfo_enemy_type3").setName("icon_type_3").setOrigin(0);
|
||||||
|
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const posParams: BattleInfoParamList = {
|
||||||
|
nameTextX: -124,
|
||||||
|
nameTextY: -11.2,
|
||||||
|
levelContainerX: -50,
|
||||||
|
levelContainerY: -5,
|
||||||
|
hpBarX: -71,
|
||||||
|
hpBarY: 4.5,
|
||||||
|
statBox: {
|
||||||
|
xOffset: 5,
|
||||||
|
paddingX: 2,
|
||||||
|
statOverflow: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
super(140, -141, false, posParams);
|
||||||
|
|
||||||
|
this.ownedIcon = globalScene.add
|
||||||
|
.sprite(0, 0, "icon_owned")
|
||||||
|
.setName("icon_owned")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0, 0)
|
||||||
|
.setPositionRelative(this.nameText, 0, 11.75);
|
||||||
|
|
||||||
|
this.championRibbon = globalScene.add
|
||||||
|
.sprite(0, 0, "champion_ribbon")
|
||||||
|
.setName("icon_champion_ribbon")
|
||||||
|
.setVisible(false)
|
||||||
|
.setOrigin(0, 0)
|
||||||
|
.setPositionRelative(this.nameText, 8, 11.75);
|
||||||
|
// Ensure these two icons are positioned below the stats container
|
||||||
|
this.addAt([this.ownedIcon, this.championRibbon], this.getIndex(this.statsContainer));
|
||||||
|
|
||||||
|
this.flyoutMenu = new BattleFlyout(this.player);
|
||||||
|
this.add(this.flyoutMenu);
|
||||||
|
|
||||||
|
this.moveBelow<Phaser.GameObjects.GameObject>(this.flyoutMenu, this.box);
|
||||||
|
|
||||||
|
this.effectivenessContainer = globalScene.add
|
||||||
|
.container(0, 0)
|
||||||
|
.setVisible(false)
|
||||||
|
.setPositionRelative(this.type1Icon, 22, 4);
|
||||||
|
this.add(this.effectivenessContainer);
|
||||||
|
|
||||||
|
this.effectivenessText = addTextObject(5, 4.5, "", TextStyle.BATTLE_INFO);
|
||||||
|
this.effectivenessWindow = addWindow(0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN);
|
||||||
|
|
||||||
|
this.effectivenessContainer.add([this.effectivenessWindow, this.effectivenessText]);
|
||||||
|
}
|
||||||
|
|
||||||
|
override initInfo(pokemon: EnemyPokemon): void {
|
||||||
|
this.flyoutMenu.initInfo(pokemon);
|
||||||
|
super.initInfo(pokemon);
|
||||||
|
|
||||||
|
if (this.nameText.visible) {
|
||||||
|
this.nameText
|
||||||
|
.on("pointerover", () =>
|
||||||
|
globalScene.ui.showTooltip(
|
||||||
|
"",
|
||||||
|
i18next.t("battleInfo:generation", {
|
||||||
|
generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on("pointerout", () => globalScene.ui.hideTooltip());
|
||||||
|
}
|
||||||
|
|
||||||
|
const dexEntry = globalScene.gameData.dexData[pokemon.species.speciesId];
|
||||||
|
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
|
||||||
|
const opponentPokemonDexAttr = pokemon.getDexAttr();
|
||||||
|
if (
|
||||||
|
globalScene.gameMode.isClassic &&
|
||||||
|
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
|
||||||
|
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
|
||||||
|
) {
|
||||||
|
this.championRibbon.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if Player owns all genders and forms of the Pokemon
|
||||||
|
const missingDexAttrs = (dexEntry.caughtAttr & opponentPokemonDexAttr) < opponentPokemonDexAttr;
|
||||||
|
|
||||||
|
const ownedAbilityAttrs = globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
|
||||||
|
|
||||||
|
// Check if the player owns ability for the root form
|
||||||
|
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
|
||||||
|
|
||||||
|
if (missingDexAttrs || !playerOwnsThisAbility) {
|
||||||
|
this.ownedIcon.setTint(0x808080);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.boss) {
|
||||||
|
this.updateBossSegmentDividers(pokemon as EnemyPokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show or hide the type effectiveness multiplier window
|
||||||
|
* Passing undefined will hide the window
|
||||||
|
*/
|
||||||
|
updateEffectiveness(effectiveness?: string) {
|
||||||
|
this.currentEffectiveness = effectiveness;
|
||||||
|
|
||||||
|
if (!globalScene.typeHints || effectiveness === undefined || this.flyoutMenu.flyoutVisible) {
|
||||||
|
this.effectivenessContainer.setVisible(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.effectivenessText.setText(effectiveness);
|
||||||
|
this.effectivenessWindow.width = 10 + this.effectivenessText.displayWidth;
|
||||||
|
this.effectivenessContainer.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the flyoutMenu to toggle if available and hides or shows the effectiveness window where necessary
|
||||||
|
*/
|
||||||
|
toggleFlyout(visible: boolean): void {
|
||||||
|
this.flyoutMenu.toggleFlyout(visible);
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
this.effectivenessContainer.setVisible(false);
|
||||||
|
} else {
|
||||||
|
this.updateEffectiveness(this.currentEffectiveness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBossSegments(pokemon: EnemyPokemon): void {
|
||||||
|
const boss = !!pokemon.bossSegments;
|
||||||
|
|
||||||
|
if (boss !== this.boss) {
|
||||||
|
this.boss = boss;
|
||||||
|
|
||||||
|
[
|
||||||
|
this.nameText,
|
||||||
|
this.genderText,
|
||||||
|
this.teraIcon,
|
||||||
|
this.splicedIcon,
|
||||||
|
this.shinyIcon,
|
||||||
|
this.ownedIcon,
|
||||||
|
this.championRibbon,
|
||||||
|
this.statusIndicator,
|
||||||
|
this.levelContainer,
|
||||||
|
this.statValuesContainer,
|
||||||
|
].map(e => (e.x += 48 * (boss ? -1 : 1)));
|
||||||
|
this.hpBar.x += 38 * (boss ? -1 : 1);
|
||||||
|
this.hpBar.y += 2 * (this.boss ? -1 : 1);
|
||||||
|
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
|
||||||
|
this.box.setTexture(this.getTextureName());
|
||||||
|
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bossSegments = boss ? pokemon.bossSegments : 0;
|
||||||
|
this.updateBossSegmentDividers(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
|
||||||
|
while (this.hpBarSegmentDividers.length) {
|
||||||
|
this.hpBarSegmentDividers.pop()?.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.boss && this.bossSegments > 1) {
|
||||||
|
const uiTheme = globalScene.uiTheme;
|
||||||
|
const maxHp = pokemon.getMaxHp();
|
||||||
|
for (let s = 1; s < this.bossSegments; s++) {
|
||||||
|
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
|
||||||
|
const divider = globalScene.add.rectangle(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
this.hpBar.height - (uiTheme ? 0 : 1),
|
||||||
|
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
|
||||||
|
);
|
||||||
|
divider.setOrigin(0.5, 0).setName("hpBar_divider_" + s.toString());
|
||||||
|
this.add(divider);
|
||||||
|
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
|
||||||
|
|
||||||
|
divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
|
||||||
|
this.hpBarSegmentDividers.push(divider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override updateStatusIcon(pokemon: EnemyPokemon): void {
|
||||||
|
super.updateStatusIcon(pokemon, (this.ownedIcon.visible ? 8 : 0) + (this.championRibbon.visible ? 8 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override updatePokemonHp(
|
||||||
|
pokemon: EnemyPokemon,
|
||||||
|
resolve: (r: void | PromiseLike<void>) => void,
|
||||||
|
instant?: boolean,
|
||||||
|
): void {
|
||||||
|
super.updatePokemonHp(pokemon, resolve, instant);
|
||||||
|
this.lastHp = pokemon.hp;
|
||||||
|
}
|
||||||
|
}
|
242
src/ui/battle-info/player-battle-info.ts
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
import { getLevelRelExp, getLevelTotalExp } from "#app/data/exp";
|
||||||
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import BattleInfo from "./battle-info";
|
||||||
|
import type { BattleInfoParamList } from "./battle-info";
|
||||||
|
|
||||||
|
export class PlayerBattleInfo extends BattleInfo {
|
||||||
|
protected player: true = true;
|
||||||
|
protected hpNumbersContainer: Phaser.GameObjects.Container;
|
||||||
|
|
||||||
|
override get statOrder(): Stat[] {
|
||||||
|
return [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
|
||||||
|
}
|
||||||
|
|
||||||
|
override getTextureName(): string {
|
||||||
|
return this.mini ? "pbinfo_player_mini" : "pbinfo_player";
|
||||||
|
}
|
||||||
|
|
||||||
|
override constructTypeIcons(): void {
|
||||||
|
this.type1Icon = globalScene.add.sprite(-139, -17, "pbinfo_player_type1").setName("icon_type_1").setOrigin(0);
|
||||||
|
this.type2Icon = globalScene.add.sprite(-139, -1, "pbinfo_player_type2").setName("icon_type_2").setOrigin(0);
|
||||||
|
this.type3Icon = globalScene.add.sprite(-154, -17, "pbinfo_player_type3").setName("icon_type_3").setOrigin(0);
|
||||||
|
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const posParams: BattleInfoParamList = {
|
||||||
|
nameTextX: -115,
|
||||||
|
nameTextY: -15.2,
|
||||||
|
levelContainerX: -41,
|
||||||
|
levelContainerY: -10,
|
||||||
|
hpBarX: -61,
|
||||||
|
hpBarY: -1,
|
||||||
|
statBox: {
|
||||||
|
xOffset: 8,
|
||||||
|
paddingX: 4,
|
||||||
|
statOverflow: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true, posParams);
|
||||||
|
|
||||||
|
this.hpNumbersContainer = globalScene.add.container(-15, 10).setName("container_hp");
|
||||||
|
|
||||||
|
// hp number container must be beneath the stat container for overlay to display properly
|
||||||
|
this.addAt(this.hpNumbersContainer, this.getIndex(this.statsContainer));
|
||||||
|
|
||||||
|
const expBar = globalScene.add.image(-98, 18, "overlay_exp").setName("overlay_exp").setOrigin(0);
|
||||||
|
this.add(expBar);
|
||||||
|
|
||||||
|
const expMaskRect = globalScene.make
|
||||||
|
.graphics({})
|
||||||
|
.setScale(6)
|
||||||
|
.fillStyle(0xffffff)
|
||||||
|
.beginPath()
|
||||||
|
.fillRect(127, 126, 85, 2);
|
||||||
|
|
||||||
|
const expMask = expMaskRect.createGeometryMask();
|
||||||
|
|
||||||
|
expBar.setMask(expMask);
|
||||||
|
|
||||||
|
this.expBar = expBar;
|
||||||
|
this.expMaskRect = expMaskRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
override initInfo(pokemon: PlayerPokemon): void {
|
||||||
|
super.initInfo(pokemon);
|
||||||
|
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
|
||||||
|
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
|
||||||
|
this.lastExp = pokemon.exp;
|
||||||
|
this.lastLevelExp = pokemon.levelExp;
|
||||||
|
|
||||||
|
this.statValuesContainer.setPosition(8, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
override setMini(mini: boolean): void {
|
||||||
|
if (this.mini === mini) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mini = mini;
|
||||||
|
|
||||||
|
this.box.setTexture(this.getTextureName());
|
||||||
|
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
|
||||||
|
|
||||||
|
if (this.player) {
|
||||||
|
this.y -= 12 * (mini ? 1 : -1);
|
||||||
|
this.baseY = this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsetElements = [
|
||||||
|
this.nameText,
|
||||||
|
this.genderText,
|
||||||
|
this.teraIcon,
|
||||||
|
this.splicedIcon,
|
||||||
|
this.shinyIcon,
|
||||||
|
this.statusIndicator,
|
||||||
|
this.levelContainer,
|
||||||
|
];
|
||||||
|
offsetElements.forEach(el => (el.y += 1.5 * (mini ? -1 : 1)));
|
||||||
|
|
||||||
|
[this.type1Icon, this.type2Icon, this.type3Icon].forEach(el => {
|
||||||
|
el.x += 4 * (mini ? 1 : -1);
|
||||||
|
el.y += -8 * (mini ? 1 : -1);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
|
||||||
|
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
|
||||||
|
|
||||||
|
const toggledElements = [this.hpNumbersContainer, this.expBar];
|
||||||
|
toggledElements.forEach(el => el.setVisible(!mini));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the Hp Number text (that is the "HP/Max HP" text that appears below the player's health bar)
|
||||||
|
* while the health bar is tweening.
|
||||||
|
* @param pokemon - The Pokemon the health bar belongs to.
|
||||||
|
*/
|
||||||
|
protected override onHpTweenUpdate(pokemon: PlayerPokemon): void {
|
||||||
|
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
|
||||||
|
this.setHpNumbers(tweenHp, pokemon.getMaxHp());
|
||||||
|
this.lastHp = tweenHp;
|
||||||
|
this.updateHpFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePokemonExp(pokemon: PlayerPokemon, instant?: boolean, levelDurationMultiplier = 1): Promise<void> {
|
||||||
|
const levelUp = this.lastLevel < pokemon.level;
|
||||||
|
const relLevelExp = getLevelRelExp(this.lastLevel + 1, pokemon.species.growthRate);
|
||||||
|
const levelExp = levelUp ? relLevelExp : pokemon.levelExp;
|
||||||
|
let ratio = relLevelExp ? levelExp / relLevelExp : 0;
|
||||||
|
if (this.lastLevel >= globalScene.getMaxExpLevel(true)) {
|
||||||
|
ratio = levelUp ? 1 : 0;
|
||||||
|
instant = true;
|
||||||
|
}
|
||||||
|
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(
|
||||||
|
1 - Math.max(this.lastLevel - 100, 0) / 150,
|
||||||
|
);
|
||||||
|
let duration =
|
||||||
|
this.visible && !instant
|
||||||
|
? ((levelExp - this.lastLevelExp) / relLevelExp) *
|
||||||
|
BattleInfo.EXP_GAINS_DURATION_BASE *
|
||||||
|
durationMultiplier *
|
||||||
|
levelDurationMultiplier
|
||||||
|
: 0;
|
||||||
|
const speed = globalScene.expGainsSpeed;
|
||||||
|
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
|
||||||
|
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
|
||||||
|
}
|
||||||
|
if (ratio === 1) {
|
||||||
|
this.lastLevelExp = 0;
|
||||||
|
this.lastLevel++;
|
||||||
|
} else {
|
||||||
|
this.lastExp = pokemon.exp;
|
||||||
|
this.lastLevelExp = pokemon.levelExp;
|
||||||
|
}
|
||||||
|
if (duration) {
|
||||||
|
globalScene.playSound("se/exp");
|
||||||
|
}
|
||||||
|
return new Promise(resolve => {
|
||||||
|
globalScene.tweens.add({
|
||||||
|
targets: this.expMaskRect,
|
||||||
|
ease: "Sine.easeIn",
|
||||||
|
x: ratio * 510,
|
||||||
|
duration: duration,
|
||||||
|
onComplete: () => {
|
||||||
|
if (!globalScene) {
|
||||||
|
return resolve();
|
||||||
|
}
|
||||||
|
if (duration) {
|
||||||
|
globalScene.sound.stopByKey("se/exp");
|
||||||
|
}
|
||||||
|
if (ratio === 1) {
|
||||||
|
globalScene.playSound("se/level_up");
|
||||||
|
this.setLevel(this.lastLevel);
|
||||||
|
globalScene.time.delayedCall(500 * levelDurationMultiplier, () => {
|
||||||
|
this.expMaskRect.x = 0;
|
||||||
|
this.updateInfo(pokemon, instant).then(() => resolve());
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the info on the info bar.
|
||||||
|
*
|
||||||
|
* In addition to performing all the steps of {@linkcode BattleInfo.updateInfo},
|
||||||
|
* it also updates the EXP Bar
|
||||||
|
*/
|
||||||
|
override async updateInfo(pokemon: PlayerPokemon, instant?: boolean): Promise<void> {
|
||||||
|
await super.updateInfo(pokemon, instant);
|
||||||
|
const isLevelCapped = pokemon.level >= globalScene.getMaxExpLevel();
|
||||||
|
const oldLevelCapped = this.lastLevelCapped;
|
||||||
|
this.lastLevelCapped = isLevelCapped;
|
||||||
|
|
||||||
|
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
|
||||||
|
const durationMultipler = Math.max(
|
||||||
|
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(1 - Math.min(pokemon.level - this.lastLevel, 10) / 10),
|
||||||
|
0.1,
|
||||||
|
);
|
||||||
|
await this.updatePokemonExp(pokemon, false, durationMultipler);
|
||||||
|
} else if (isLevelCapped !== oldLevelCapped) {
|
||||||
|
this.setLevel(pokemon.level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HP numbers text, that is the "HP/Max HP" text that appears below the player's health bar.
|
||||||
|
* @param hp - The current HP of the player.
|
||||||
|
* @param maxHp - The maximum HP of the player.
|
||||||
|
*/
|
||||||
|
setHpNumbers(hp: number, maxHp: number): void {
|
||||||
|
if (!globalScene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hpNumbersContainer.removeAll(true);
|
||||||
|
const hpStr = hp.toString();
|
||||||
|
const maxHpStr = maxHp.toString();
|
||||||
|
let offset = 0;
|
||||||
|
for (let i = maxHpStr.length - 1; i >= 0; i--) {
|
||||||
|
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", maxHpStr[i]));
|
||||||
|
}
|
||||||
|
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", "/"));
|
||||||
|
for (let i = hpStr.length - 1; i >= 0; i--) {
|
||||||
|
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", hpStr[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the level numbers container to display the provided level
|
||||||
|
*
|
||||||
|
* Overrides the default implementation to handle displaying level capped numbers in red.
|
||||||
|
* @param level - The level to display
|
||||||
|
*/
|
||||||
|
override setLevel(level: number): void {
|
||||||
|
super.setLevel(level, level >= globalScene.getMaxExpLevel() ? "numbers_red" : "numbers");
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils/common";
|
|||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import type { PokemonMove } from "#app/field/pokemon";
|
import type { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { CommandPhase } from "#app/phases/command-phase";
|
import type { CommandPhase } from "#app/phases/command-phase";
|
||||||
import MoveInfoOverlay from "./move-info-overlay";
|
import MoveInfoOverlay from "./move-info-overlay";
|
||||||
@ -279,7 +279,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
|||||||
this.moveInfoOverlay.show(pokemonMove.getMove());
|
this.moveInfoOverlay.show(pokemonMove.getMove());
|
||||||
|
|
||||||
pokemon.getOpponents().forEach(opponent => {
|
pokemon.getOpponents().forEach(opponent => {
|
||||||
opponent.updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
|
(opponent as EnemyPokemon).updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +391,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
|||||||
|
|
||||||
const opponents = (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents();
|
const opponents = (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents();
|
||||||
opponents.forEach(opponent => {
|
opponents.forEach(opponent => {
|
||||||
opponent.updateEffectiveness(undefined);
|
(opponent as EnemyPokemon).updateEffectiveness();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import type Pokemon from "../field/pokemon";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { DexEntry, StarterDataEntry } from "../system/game-data";
|
import type { DexEntry, StarterDataEntry } from "../system/game-data";
|
||||||
import { DexAttr } from "../system/game-data";
|
import { DexAttr } from "../system/game-data";
|
||||||
import { fixedInt } from "#app/utils/common";
|
import { fixedInt, getShinyDescriptor } from "#app/utils/common";
|
||||||
import ConfirmUiHandler from "./confirm-ui-handler";
|
import ConfirmUiHandler from "./confirm-ui-handler";
|
||||||
import { StatsContainer } from "./stats-container";
|
import { StatsContainer } from "./stats-container";
|
||||||
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
|
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
|
||||||
@ -343,18 +343,19 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
this.pokemonShinyIcon.setVisible(pokemon.isShiny());
|
this.pokemonShinyIcon.setVisible(pokemon.isShiny());
|
||||||
this.pokemonShinyIcon.setTint(getVariantTint(baseVariant));
|
this.pokemonShinyIcon.setTint(getVariantTint(baseVariant));
|
||||||
if (this.pokemonShinyIcon.visible) {
|
if (this.pokemonShinyIcon.visible) {
|
||||||
const shinyDescriptor =
|
let shinyDescriptor = "";
|
||||||
doubleShiny || baseVariant
|
if (doubleShiny || baseVariant) {
|
||||||
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
|
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
|
||||||
: "";
|
if (doubleShiny) {
|
||||||
this.pokemonShinyIcon.on("pointerover", () =>
|
shinyDescriptor += "/" + getShinyDescriptor(pokemon.fusionVariant);
|
||||||
globalScene.ui.showTooltip(
|
}
|
||||||
"",
|
shinyDescriptor += ")";
|
||||||
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
|
}
|
||||||
true,
|
this.pokemonShinyIcon
|
||||||
),
|
.on("pointerover", () =>
|
||||||
);
|
globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor, true),
|
||||||
this.pokemonShinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
|
)
|
||||||
|
.on("pointerout", () => globalScene.ui.hideTooltip());
|
||||||
|
|
||||||
const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0));
|
const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0));
|
||||||
const newVariant = BigInt(1 << (pokemon.variant + 4));
|
const newVariant = BigInt(1 << (pokemon.variant + 4));
|
||||||
|
@ -108,17 +108,21 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
|||||||
instructionTextSize: "38px",
|
instructionTextSize: "38px",
|
||||||
},
|
},
|
||||||
de: {
|
de: {
|
||||||
starterInfoTextSize: "48px",
|
starterInfoTextSize: "54px",
|
||||||
instructionTextSize: "35px",
|
instructionTextSize: "35px",
|
||||||
starterInfoXPos: 33,
|
starterInfoXPos: 35,
|
||||||
},
|
},
|
||||||
"es-ES": {
|
"es-ES": {
|
||||||
starterInfoTextSize: "52px",
|
starterInfoTextSize: "50px",
|
||||||
instructionTextSize: "35px",
|
instructionTextSize: "38px",
|
||||||
|
starterInfoYOffset: 0.5,
|
||||||
|
starterInfoXPos: 38,
|
||||||
},
|
},
|
||||||
"es-MX": {
|
"es-MX": {
|
||||||
starterInfoTextSize: "52px",
|
starterInfoTextSize: "50px",
|
||||||
instructionTextSize: "35px",
|
instructionTextSize: "38px",
|
||||||
|
starterInfoYOffset: 0.5,
|
||||||
|
starterInfoXPos: 38,
|
||||||
},
|
},
|
||||||
fr: {
|
fr: {
|
||||||
starterInfoTextSize: "54px",
|
starterInfoTextSize: "54px",
|
||||||
@ -128,21 +132,16 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
|||||||
starterInfoTextSize: "56px",
|
starterInfoTextSize: "56px",
|
||||||
instructionTextSize: "38px",
|
instructionTextSize: "38px",
|
||||||
},
|
},
|
||||||
pt_BR: {
|
"pt-BR": {
|
||||||
starterInfoTextSize: "47px",
|
starterInfoTextSize: "48px",
|
||||||
instructionTextSize: "38px",
|
instructionTextSize: "42px",
|
||||||
|
starterInfoYOffset: 0.5,
|
||||||
starterInfoXPos: 33,
|
starterInfoXPos: 33,
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
starterInfoTextSize: "47px",
|
starterInfoTextSize: "56px",
|
||||||
instructionTextSize: "38px",
|
instructionTextSize: "36px",
|
||||||
starterInfoYOffset: 1,
|
starterInfoXPos: 26,
|
||||||
starterInfoXPos: 24,
|
|
||||||
},
|
|
||||||
pt: {
|
|
||||||
starterInfoTextSize: "48px",
|
|
||||||
instructionTextSize: "42px",
|
|
||||||
starterInfoXPos: 33,
|
|
||||||
},
|
},
|
||||||
ko: {
|
ko: {
|
||||||
starterInfoTextSize: "60px",
|
starterInfoTextSize: "60px",
|
||||||
@ -156,9 +155,11 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
|||||||
starterInfoYOffset: 0.5,
|
starterInfoYOffset: 0.5,
|
||||||
starterInfoXPos: 33,
|
starterInfoXPos: 33,
|
||||||
},
|
},
|
||||||
"ca-ES": {
|
ca: {
|
||||||
starterInfoTextSize: "52px",
|
starterInfoTextSize: "48px",
|
||||||
instructionTextSize: "38px",
|
instructionTextSize: "38px",
|
||||||
|
starterInfoYOffset: 0.5,
|
||||||
|
starterInfoXPos: 29,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
isNullOrUndefined,
|
isNullOrUndefined,
|
||||||
toReadableString,
|
toReadableString,
|
||||||
formatStat,
|
formatStat,
|
||||||
|
getShinyDescriptor,
|
||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
|
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
@ -444,18 +445,19 @@ export default class SummaryUiHandler extends UiHandler {
|
|||||||
this.shinyIcon.setVisible(this.pokemon.isShiny(false));
|
this.shinyIcon.setVisible(this.pokemon.isShiny(false));
|
||||||
this.shinyIcon.setTint(getVariantTint(baseVariant));
|
this.shinyIcon.setTint(getVariantTint(baseVariant));
|
||||||
if (this.shinyIcon.visible) {
|
if (this.shinyIcon.visible) {
|
||||||
const shinyDescriptor =
|
let shinyDescriptor = "";
|
||||||
doubleShiny || baseVariant
|
if (doubleShiny || baseVariant) {
|
||||||
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${this.pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : this.pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
|
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
|
||||||
: "";
|
if (doubleShiny) {
|
||||||
this.shinyIcon.on("pointerover", () =>
|
shinyDescriptor += "/" + getShinyDescriptor(this.pokemon.fusionVariant);
|
||||||
globalScene.ui.showTooltip(
|
}
|
||||||
"",
|
shinyDescriptor += ")";
|
||||||
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
|
}
|
||||||
true,
|
this.shinyIcon
|
||||||
),
|
.on("pointerover", () =>
|
||||||
);
|
globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor, true),
|
||||||
this.shinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
|
)
|
||||||
|
.on("pointerout", () => globalScene.ui.hideTooltip());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
|
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
|
||||||
|
@ -2,6 +2,7 @@ import { MoneyFormat } from "#enums/money-format";
|
|||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||||
|
import type { Variant } from "#app/sprites/variant";
|
||||||
|
|
||||||
export type nil = null | undefined;
|
export type nil = null | undefined;
|
||||||
|
|
||||||
@ -576,3 +577,18 @@ export function animationFileName(move: Moves): string {
|
|||||||
export function camelCaseToKebabCase(str: string): string {
|
export function camelCaseToKebabCase(str: string): string {
|
||||||
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase());
|
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the localized shiny descriptor for the provided variant
|
||||||
|
* @param variant - The variant to get the shiny descriptor for
|
||||||
|
* @returns The localized shiny descriptor
|
||||||
|
*/
|
||||||
|
export function getShinyDescriptor(variant: Variant): string {
|
||||||
|
switch (variant) {
|
||||||
|
case 2:
|
||||||
|
return i18next.t("common:epicShiny");
|
||||||
|
case 1:
|
||||||
|
return i18next.t("common:rareShiny");
|
||||||
|
case 0:
|
||||||
|
return i18next.t("common:commonShiny");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,7 +25,7 @@ describe("Moves - Gastro Acid", () => {
|
|||||||
game.override.battleStyle("double");
|
game.override.battleStyle("double");
|
||||||
game.override.startingLevel(1);
|
game.override.startingLevel(1);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.ability(Abilities.NONE);
|
game.override.ability(Abilities.BALL_FETCH);
|
||||||
game.override.moveset([Moves.GASTRO_ACID, Moves.WATER_GUN, Moves.SPLASH, Moves.CORE_ENFORCER]);
|
game.override.moveset([Moves.GASTRO_ACID, Moves.WATER_GUN, Moves.SPLASH, Moves.CORE_ENFORCER]);
|
||||||
game.override.enemySpecies(Species.BIDOOF);
|
game.override.enemySpecies(Species.BIDOOF);
|
||||||
game.override.enemyMoveset(Moves.SPLASH);
|
game.override.enemyMoveset(Moves.SPLASH);
|
||||||
@ -40,7 +40,7 @@ describe("Moves - Gastro Acid", () => {
|
|||||||
* - player mon 1 should have dealt damage, player mon 2 should have not
|
* - player mon 1 should have dealt damage, player mon 2 should have not
|
||||||
*/
|
*/
|
||||||
|
|
||||||
await game.startBattle();
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
game.move.select(Moves.GASTRO_ACID, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.GASTRO_ACID, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
@ -63,7 +63,7 @@ describe("Moves - Gastro Acid", () => {
|
|||||||
it("fails if used on an enemy with an already-suppressed ability", async () => {
|
it("fails if used on an enemy with an already-suppressed ability", async () => {
|
||||||
game.override.battleStyle("single");
|
game.override.battleStyle("single");
|
||||||
|
|
||||||
await game.startBattle();
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
game.move.select(Moves.CORE_ENFORCER);
|
game.move.select(Moves.CORE_ENFORCER);
|
||||||
// Force player to be slower to enable Core Enforcer to proc its suppression effect
|
// Force player to be slower to enable Core Enforcer to proc its suppression effect
|
||||||
@ -77,4 +77,27 @@ describe("Moves - Gastro Acid", () => {
|
|||||||
|
|
||||||
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should suppress the passive of a target even if its main ability is unsuppressable and not suppress main abli", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyAbility(Abilities.COMATOSE)
|
||||||
|
.enemyPassiveAbility(Abilities.WATER_ABSORB)
|
||||||
|
.moveset([Moves.SPLASH, Moves.GASTRO_ACID, Moves.WATER_GUN]);
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||||
|
|
||||||
|
game.move.select(Moves.GASTRO_ACID);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(enemyPokemon?.summonData.abilitySuppressed).toBe(true);
|
||||||
|
|
||||||
|
game.move.select(Moves.WATER_GUN);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(enemyPokemon?.getHpRatio()).toBeLessThan(1);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPORE);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(enemyPokemon?.status?.effect).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export interface MockGameObject {
|
export interface MockGameObject {
|
||||||
name: string;
|
name: string;
|
||||||
|
destroy?(): void;
|
||||||
}
|
}
|
||||||
|
@ -2,199 +2,256 @@ import type MockTextureManager from "#test/testUtils/mocks/mockTextureManager";
|
|||||||
import type { MockGameObject } from "../mockGameObject";
|
import type { MockGameObject } from "../mockGameObject";
|
||||||
|
|
||||||
export default class MockContainer implements MockGameObject {
|
export default class MockContainer implements MockGameObject {
|
||||||
protected x;
|
protected x: number;
|
||||||
protected y;
|
protected y: number;
|
||||||
protected scene;
|
protected scene;
|
||||||
protected width;
|
protected width: number;
|
||||||
protected height;
|
protected height: number;
|
||||||
protected visible;
|
protected visible: boolean;
|
||||||
private alpha;
|
private alpha: number;
|
||||||
private style;
|
private style;
|
||||||
public frame;
|
public frame;
|
||||||
protected textureManager;
|
protected textureManager;
|
||||||
public list: MockGameObject[] = [];
|
public list: MockGameObject[] = [];
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
constructor(textureManager: MockTextureManager, x, y) {
|
constructor(textureManager: MockTextureManager, x: number, y: number) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.frame = {};
|
this.frame = {};
|
||||||
this.textureManager = textureManager;
|
this.textureManager = textureManager;
|
||||||
}
|
}
|
||||||
setVisible(visible) {
|
setVisible(visible: boolean): this {
|
||||||
this.visible = visible;
|
this.visible = visible;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
once(_event, _callback, _source) {}
|
once(_event, _callback, _source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
off(_event, _callback, _source) {}
|
off(_event, _callback, _source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
removeFromDisplayList() {
|
removeFromDisplayList(): this {
|
||||||
// same as remove or destroy
|
// same as remove or destroy
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeBetween(_startIndex, _endIndex, _destroyChild) {
|
removeBetween(_startIndex, _endIndex, _destroyChild): this {
|
||||||
// Removes multiple children across an index range
|
// Removes multiple children across an index range
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addedToScene() {
|
addedToScene() {
|
||||||
// This callback is invoked when this Game Object is added to a Scene.
|
// This callback is invoked when this Game Object is added to a Scene.
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(_width, _height) {
|
setSize(_width: number, _height: number): this {
|
||||||
// Sets the size of this Game Object.
|
// Sets the size of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMask() {
|
setMask(): this {
|
||||||
/// Sets the mask that this Game Object will use to render with.
|
/// Sets the mask that this Game Object will use to render with.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPositionRelative(_source, _x, _y) {
|
setPositionRelative(_source, _x, _y): this {
|
||||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setInteractive = () => null;
|
setInteractive(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setOrigin(x, y) {
|
setOrigin(x = 0.5, y = x): this {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAlpha(alpha) {
|
setAlpha(alpha = 1): this {
|
||||||
this.alpha = alpha;
|
this.alpha = alpha;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFrame(_frame, _updateSize?: boolean, _updateOrigin?: boolean) {
|
setFrame(_frame, _updateSize?: boolean, _updateOrigin?: boolean): this {
|
||||||
// Sets the frame this Game Object will use to render with.
|
// Sets the frame this Game Object will use to render with.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setScale(_scale) {
|
setScale(_x = 1, _y = _x): this {
|
||||||
// Sets the scale of this Game Object.
|
// Sets the scale of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPosition(x, y) {
|
setPosition(x = 0, y = x, _z = 0, _w = 0): this {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setX(x) {
|
setX(x = 0): this {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setY(y) {
|
setY(y = 0): this {
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.list = [];
|
this.list = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
setShadow(_shadowXpos, _shadowYpos, _shadowColor) {
|
setShadow(_shadowXpos, _shadowYpos, _shadowColor): this {
|
||||||
// Sets the shadow settings for this Game Object.
|
// Sets the shadow settings for this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLineSpacing(_lineSpacing) {
|
setLineSpacing(_lineSpacing): this {
|
||||||
// Sets the line spacing value of this Game Object.
|
// Sets the line spacing value of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setText(_text) {
|
setText(_text): this {
|
||||||
// Sets the text this Game Object will display.
|
// Sets the text this Game Object will display.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAngle(_angle) {
|
setAngle(_angle): this {
|
||||||
// Sets the angle of this Game Object.
|
// Sets the angle of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setShadowOffset(_offsetX, _offsetY) {
|
setShadowOffset(_offsetX, _offsetY): this {
|
||||||
// Sets the shadow offset values.
|
// Sets the shadow offset values.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWordWrapWidth(_width) {
|
setWordWrapWidth(_width) {
|
||||||
// Sets the width (in pixels) to use for wrapping lines.
|
// Sets the width (in pixels) to use for wrapping lines.
|
||||||
}
|
}
|
||||||
|
|
||||||
setFontSize(_fontSize) {
|
setFontSize(_fontSize): this {
|
||||||
// Sets the font size of this Game Object.
|
// Sets the font size of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
getBounds() {
|
getBounds() {
|
||||||
return { width: this.width, height: this.height };
|
return { width: this.width, height: this.height };
|
||||||
}
|
}
|
||||||
|
|
||||||
setColor(_color) {
|
setColor(_color): this {
|
||||||
// Sets the tint of this Game Object.
|
// Sets the tint of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setShadowColor(_color) {
|
setShadowColor(_color): this {
|
||||||
// Sets the shadow color.
|
// Sets the shadow color.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTint(_color) {
|
setTint(_color: this) {
|
||||||
// Sets the tint of this Game Object.
|
// Sets the tint of this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStrokeStyle(_thickness, _color) {
|
setStrokeStyle(_thickness, _color): this {
|
||||||
// Sets the stroke style for the graphics.
|
// Sets the stroke style for the graphics.
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDepth(_depth) {
|
setDepth(_depth): this {
|
||||||
// Sets the depth of this Game Object.
|
// Sets the depth of this Game Object.\
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTexture(_texture) {
|
setTexture(_texture): this {
|
||||||
// Sets the texture this Game Object will use to render with.
|
// Sets the texture this Game Object will use to render with.\
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTint() {
|
clearTint(): this {
|
||||||
// Clears any previously set tint.
|
// Clears any previously set tint.\
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToBack() {
|
sendToBack(): this {
|
||||||
// Sends this Game Object to the back of its parent's display list.
|
// Sends this Game Object to the back of its parent's display list.\
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveTo(_obj) {
|
moveTo(_obj): this {
|
||||||
// Moves this Game Object to the given index in the list.
|
// Moves this Game Object to the given index in the list.\
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveAbove(_obj) {
|
moveAbove(_obj): this {
|
||||||
// Moves this Game Object to be above the given Game Object in the display list.
|
// Moves this Game Object to be above the given Game Object in the display list.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveBelow(_obj) {
|
moveBelow(_obj): this {
|
||||||
// Moves this Game Object to be below the given Game Object in the display list.
|
// Moves this Game Object to be below the given Game Object in the display list.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(name: string) {
|
setName(name: string): this {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bringToTop(_obj) {
|
bringToTop(_obj): this {
|
||||||
// Brings this Game Object to the top of its parents display list.
|
// Brings this Game Object to the top of its parents display list.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
on(_event, _callback, _source) {}
|
on(_event, _callback, _source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
add(obj) {
|
add(obj: MockGameObject | MockGameObject[]): this {
|
||||||
// Adds a child to this Game Object.
|
if (Array.isArray(obj)) {
|
||||||
|
this.list.push(...obj);
|
||||||
|
} else {
|
||||||
this.list.push(obj);
|
this.list.push(obj);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
removeAll() {
|
removeAll(): this {
|
||||||
// Removes all Game Objects from this Container.
|
// Removes all Game Objects from this Container.
|
||||||
this.list = [];
|
this.list = [];
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addAt(obj, index) {
|
addAt(obj: MockGameObject | MockGameObject[], index = 0): this {
|
||||||
// Adds a Game Object to this Container at the given index.
|
// Adds a Game Object to this Container at the given index.
|
||||||
this.list.splice(index, 0, obj);
|
if (!Array.isArray(obj)) {
|
||||||
|
obj = [obj];
|
||||||
|
}
|
||||||
|
this.list.splice(index, 0, ...obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(obj) {
|
remove(obj: MockGameObject | MockGameObject[], destroyChild = false): this {
|
||||||
const index = this.list.indexOf(obj);
|
if (!Array.isArray(obj)) {
|
||||||
|
obj = [obj];
|
||||||
|
}
|
||||||
|
for (const item of obj) {
|
||||||
|
const index = this.list.indexOf(item);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.list.splice(index, 1);
|
this.list.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
if (destroyChild) {
|
||||||
|
item.destroy?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndex(obj) {
|
getIndex(obj) {
|
||||||
@ -210,15 +267,43 @@ export default class MockContainer implements MockGameObject {
|
|||||||
return this.list;
|
return this.list;
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(key: string) {
|
getByName(key: string): MockGameObject | null {
|
||||||
return this.list.find(v => v.name === key) ?? new MockContainer(this.textureManager, 0, 0);
|
return this.list.find(v => v.name === key) ?? new MockContainer(this.textureManager, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
disableInteractive = () => null;
|
disableInteractive(): this {
|
||||||
|
return this;
|
||||||
each(method) {
|
|
||||||
for (const item of this.list) {
|
|
||||||
method(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noBannedTypes: This matches the signature of the method it mocks
|
||||||
|
each(callback: Function, context?: object, ...args: any[]): this {
|
||||||
|
if (context !== undefined) {
|
||||||
|
callback = callback.bind(context);
|
||||||
|
}
|
||||||
|
for (const item of this.list.slice()) {
|
||||||
|
callback(item, ...args);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noBannedTypes: This matches the signature of the method it mocks
|
||||||
|
iterate(callback: Function, context?: object, ...args: any[]): this {
|
||||||
|
if (context !== undefined) {
|
||||||
|
callback = callback.bind(context);
|
||||||
|
}
|
||||||
|
for (const item of this.list) {
|
||||||
|
callback(item, ...args);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
copyPosition(source: { x?: number; y?: number }): this {
|
||||||
|
if (source.x !== undefined) {
|
||||||
|
this.x = source.x;
|
||||||
|
}
|
||||||
|
if (source.y !== undefined) {
|
||||||
|
this.y = source.y;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,57 +8,76 @@ export default class MockGraphics implements MockGameObject {
|
|||||||
this.scene = textureManager.scene;
|
this.scene = textureManager.scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
fillStyle(_color) {
|
fillStyle(_color): this {
|
||||||
// Sets the fill style to be used by the fill methods.
|
// Sets the fill style to be used by the fill methods.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginPath() {
|
beginPath(): this {
|
||||||
// Starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path.
|
// Starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
fillRect(_x, _y, _width, _height) {
|
fillRect(_x, _y, _width, _height): this {
|
||||||
// Adds a rectangle shape to the path which is filled when you call fill().
|
// Adds a rectangle shape to the path which is filled when you call fill().
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
createGeometryMask() {
|
createGeometryMask(): this {
|
||||||
// Creates a geometry mask.
|
// Creates a geometry mask.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setOrigin(_x, _y) {}
|
setOrigin(_x, _y): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setAlpha(_alpha) {}
|
setAlpha(_alpha): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setVisible(_visible) {}
|
setVisible(_visible): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setName(_name) {}
|
setName(_name) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
once(_event, _callback, _source) {}
|
once(_event, _callback, _source) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
removeFromDisplayList() {
|
removeFromDisplayList(): this {
|
||||||
// same as remove or destroy
|
// same as remove or destroy
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addedToScene() {
|
addedToScene() {
|
||||||
// This callback is invoked when this Game Object is added to a Scene.
|
// This callback is invoked when this Game Object is added to a Scene.
|
||||||
}
|
}
|
||||||
|
|
||||||
setPositionRelative(_source, _x, _y) {
|
setPositionRelative(_source, _x, _y): this {
|
||||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.list = [];
|
this.list = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
setScale(_scale) {
|
setScale(_scale): this {
|
||||||
// Sets the scale of this Game Object.
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
off(_event, _callback, _source) {}
|
off(_event, _callback, _source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
add(obj) {
|
add(obj): this {
|
||||||
// Adds a child to this Game Object.
|
// Adds a child to this Game Object.
|
||||||
this.list.push(obj);
|
this.list.push(obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll() {
|
removeAll() {
|
||||||
@ -90,4 +109,8 @@ export default class MockGraphics implements MockGameObject {
|
|||||||
getAll() {
|
getAll() {
|
||||||
return this.list;
|
return this.list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copyPosition(_source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,51 +10,70 @@ export default class MockRectangle implements MockGameObject {
|
|||||||
this.fillColor = fillColor;
|
this.fillColor = fillColor;
|
||||||
this.scene = textureManager.scene;
|
this.scene = textureManager.scene;
|
||||||
}
|
}
|
||||||
setOrigin(_x, _y) {}
|
setOrigin(_x, _y): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setAlpha(_alpha) {}
|
setAlpha(_alpha): this {
|
||||||
setVisible(_visible) {}
|
return this;
|
||||||
|
}
|
||||||
|
setVisible(_visible): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setName(_name) {}
|
setName(_name): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
once(_event, _callback, _source) {}
|
once(_event, _callback, _source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
removeFromDisplayList() {
|
removeFromDisplayList(): this {
|
||||||
// same as remove or destroy
|
// same as remove or destroy
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addedToScene() {
|
addedToScene() {
|
||||||
// This callback is invoked when this Game Object is added to a Scene.
|
// This callback is invoked when this Game Object is added to a Scene.
|
||||||
}
|
}
|
||||||
|
|
||||||
setPositionRelative(_source, _x, _y) {
|
setPositionRelative(_source, _x, _y): this {
|
||||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.list = [];
|
this.list = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
add(obj) {
|
add(obj: MockGameObject | MockGameObject[]): this {
|
||||||
// Adds a child to this Game Object.
|
// Adds a child to this Game Object.
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
this.list.push(...obj);
|
||||||
|
} else {
|
||||||
this.list.push(obj);
|
this.list.push(obj);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
removeAll() {
|
removeAll() {
|
||||||
// Removes all Game Objects from this Container.
|
// Removes all Game Objects from this Container.
|
||||||
this.list = [];
|
this.list = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
addAt(obj, index) {
|
addAt(obj, index): this {
|
||||||
// Adds a Game Object to this Container at the given index.
|
// Adds a Game Object to this Container at the given index.
|
||||||
this.list.splice(index, 0, obj);
|
this.list.splice(index, 0, obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(obj) {
|
remove(obj): this {
|
||||||
const index = this.list.indexOf(obj);
|
const index = this.list.indexOf(obj);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.list.splice(index, 1);
|
this.list.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndex(obj) {
|
getIndex(obj) {
|
||||||
@ -69,9 +88,12 @@ export default class MockRectangle implements MockGameObject {
|
|||||||
getAll() {
|
getAll() {
|
||||||
return this.list;
|
return this.list;
|
||||||
}
|
}
|
||||||
setScale(_scale) {
|
setScale(_scale): this {
|
||||||
// return this.phaserText.setScale(scale);
|
// return this.phaserText.setScale(scale);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
off() {}
|
off(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import type { MockGameObject } from "../mockGameObject";
|
import type { MockGameObject } from "../mockGameObject";
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
|
||||||
import Frame = Phaser.Textures.Frame;
|
import Frame = Phaser.Textures.Frame;
|
||||||
|
|
||||||
export default class MockSprite implements MockGameObject {
|
export default class MockSprite implements MockGameObject {
|
||||||
@ -21,7 +20,9 @@ export default class MockSprite implements MockGameObject {
|
|||||||
Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive;
|
Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture;
|
Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture;
|
||||||
|
// @ts-ignore
|
||||||
Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame;
|
Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame;
|
||||||
|
// @ts-ignore
|
||||||
Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame;
|
Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame;
|
||||||
// Phaser.GameObjects.Sprite.prototype.disable = this.disable;
|
// Phaser.GameObjects.Sprite.prototype.disable = this.disable;
|
||||||
|
|
||||||
@ -37,46 +38,55 @@ export default class MockSprite implements MockGameObject {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setTexture(_key: string, _frame?: string | number) {
|
setTexture(_key: string, _frame?: string | number): this {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSizeToFrame(_frame?: boolean | Frame): Sprite {
|
setSizeToFrame(_frame?: boolean | Frame): this {
|
||||||
return {} as Sprite;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPipeline(obj) {
|
setPipeline(obj): this {
|
||||||
// Sets the pipeline of this Game Object.
|
// Sets the pipeline of this Game Object.
|
||||||
return this.phaserSprite.setPipeline(obj);
|
this.phaserSprite.setPipeline(obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
off(_event, _callback, _source) {}
|
off(_event, _callback, _source): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setTintFill(color) {
|
setTintFill(color): this {
|
||||||
// Sets the tint fill color.
|
// Sets the tint fill color.
|
||||||
return this.phaserSprite.setTintFill(color);
|
this.phaserSprite.setTintFill(color);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setScale(scale) {
|
setScale(scale = 1): this {
|
||||||
return this.phaserSprite.setScale(scale);
|
this.phaserSprite.setScale(scale);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setOrigin(x, y) {
|
setOrigin(x = 0.5, y = x): this {
|
||||||
return this.phaserSprite.setOrigin(x, y);
|
this.phaserSprite.setOrigin(x, y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(width, height) {
|
setSize(width, height): this {
|
||||||
// Sets the size of this Game Object.
|
// Sets the size of this Game Object.
|
||||||
return this.phaserSprite.setSize(width, height);
|
this.phaserSprite.setSize(width, height);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
once(event, callback, source) {
|
once(event, callback, source): this {
|
||||||
return this.phaserSprite.once(event, callback, source);
|
this.phaserSprite.once(event, callback, source);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromDisplayList() {
|
removeFromDisplayList(): this {
|
||||||
// same as remove or destroy
|
// same as remove or destroy
|
||||||
return this.phaserSprite.removeFromDisplayList();
|
this.phaserSprite.removeFromDisplayList();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addedToScene() {
|
addedToScene() {
|
||||||
@ -84,114 +94,140 @@ export default class MockSprite implements MockGameObject {
|
|||||||
return this.phaserSprite.addedToScene();
|
return this.phaserSprite.addedToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisible(visible) {
|
setVisible(visible): this {
|
||||||
return this.phaserSprite.setVisible(visible);
|
this.phaserSprite.setVisible(visible);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPosition(x, y) {
|
setPosition(x, y): this {
|
||||||
return this.phaserSprite.setPosition(x, y);
|
this.phaserSprite.setPosition(x, y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setRotation(radians) {
|
setRotation(radians): this {
|
||||||
return this.phaserSprite.setRotation(radians);
|
this.phaserSprite.setRotation(radians);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop(): this {
|
||||||
return this.phaserSprite.stop();
|
this.phaserSprite.stop();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setInteractive = () => null;
|
setInteractive(): this {
|
||||||
|
return this;
|
||||||
on(event, callback, source) {
|
|
||||||
return this.phaserSprite.on(event, callback, source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAlpha(alpha) {
|
on(event, callback, source): this {
|
||||||
return this.phaserSprite.setAlpha(alpha);
|
this.phaserSprite.on(event, callback, source);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTint(color) {
|
setAlpha(alpha): this {
|
||||||
|
this.phaserSprite.setAlpha(alpha);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTint(color): this {
|
||||||
// Sets the tint of this Game Object.
|
// Sets the tint of this Game Object.
|
||||||
return this.phaserSprite.setTint(color);
|
this.phaserSprite.setTint(color);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFrame(frame, _updateSize?: boolean, _updateOrigin?: boolean) {
|
setFrame(frame, _updateSize?: boolean, _updateOrigin?: boolean): this {
|
||||||
// Sets the frame this Game Object will use to render with.
|
// Sets the frame this Game Object will use to render with.
|
||||||
this.frame = frame;
|
this.frame = frame;
|
||||||
return frame;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPositionRelative(source, x, y) {
|
setPositionRelative(source, x, y): this {
|
||||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||||
return this.phaserSprite.setPositionRelative(source, x, y);
|
this.phaserSprite.setPositionRelative(source, x, y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setY(y) {
|
setY(y: number): this {
|
||||||
return this.phaserSprite.setY(y);
|
this.phaserSprite.setY(y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCrop(x, y, width, height) {
|
setCrop(x: number, y: number, width: number, height: number): this {
|
||||||
// Sets the crop size of this Game Object.
|
// Sets the crop size of this Game Object.
|
||||||
return this.phaserSprite.setCrop(x, y, width, height);
|
this.phaserSprite.setCrop(x, y, width, height);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTint() {
|
clearTint(): this {
|
||||||
// Clears any previously set tint.
|
// Clears any previously set tint.
|
||||||
return this.phaserSprite.clearTint();
|
this.phaserSprite.clearTint();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
disableInteractive() {
|
disableInteractive(): this {
|
||||||
// Disables Interactive features of this Game Object.
|
// Disables Interactive features of this Game Object.
|
||||||
return null;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply() {
|
apply() {
|
||||||
return this.phaserSprite.apply();
|
this.phaserSprite.apply();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
play() {
|
play(): this {
|
||||||
// return this.phaserSprite.play();
|
// return this.phaserSprite.play();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPipelineData(key, value) {
|
setPipelineData(key: string, value: any): this {
|
||||||
this.pipelineData[key] = value;
|
this.pipelineData[key] = value;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
return this.phaserSprite.destroy();
|
return this.phaserSprite.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(name) {
|
setName(name: string): this {
|
||||||
return this.phaserSprite.setName(name);
|
this.phaserSprite.setName(name);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAngle(angle) {
|
setAngle(angle): this {
|
||||||
return this.phaserSprite.setAngle(angle);
|
this.phaserSprite.setAngle(angle);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMask() {}
|
setMask(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
add(obj) {
|
add(obj: MockGameObject | MockGameObject[]): this {
|
||||||
// Adds a child to this Game Object.
|
// Adds a child to this Game Object.
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
this.list.push(...obj);
|
||||||
|
} else {
|
||||||
this.list.push(obj);
|
this.list.push(obj);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
removeAll() {
|
removeAll() {
|
||||||
// Removes all Game Objects from this Container.
|
// Removes all Game Objects from this Container.
|
||||||
this.list = [];
|
this.list = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
addAt(obj, index) {
|
addAt(obj, index): this {
|
||||||
// Adds a Game Object to this Container at the given index.
|
// Adds a Game Object to this Container at the given index.
|
||||||
this.list.splice(index, 0, obj);
|
this.list.splice(index, 0, obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(obj) {
|
remove(obj): this {
|
||||||
const index = this.list.indexOf(obj);
|
const index = this.list.indexOf(obj);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.list.splice(index, 1);
|
this.list.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndex(obj) {
|
getIndex(obj) {
|
||||||
@ -206,4 +242,9 @@ export default class MockSprite implements MockGameObject {
|
|||||||
getAll() {
|
getAll() {
|
||||||
return this.list;
|
return this.list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copyPosition(obj): this {
|
||||||
|
this.phaserSprite.copyPosition(obj);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,42 +107,51 @@ export default class MockText implements MockGameObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setScale(_scale) {
|
setScale(_scale): this {
|
||||||
// return this.phaserText.setScale(scale);
|
// return this.phaserText.setScale(scale);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setShadow(_shadowXpos, _shadowYpos, _shadowColor) {
|
setShadow(_shadowXpos, _shadowYpos, _shadowColor): this {
|
||||||
// Sets the shadow settings for this Game Object.
|
// Sets the shadow settings for this Game Object.
|
||||||
// return this.phaserText.setShadow(shadowXpos, shadowYpos, shadowColor);
|
// return this.phaserText.setShadow(shadowXpos, shadowYpos, shadowColor);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLineSpacing(_lineSpacing) {
|
setLineSpacing(_lineSpacing): this {
|
||||||
// Sets the line spacing value of this Game Object.
|
// Sets the line spacing value of this Game Object.
|
||||||
// return this.phaserText.setLineSpacing(lineSpacing);
|
// return this.phaserText.setLineSpacing(lineSpacing);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setOrigin(_x, _y) {
|
setOrigin(_x, _y): this {
|
||||||
// return this.phaserText.setOrigin(x, y);
|
// return this.phaserText.setOrigin(x, y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
once(_event, _callback, _source) {
|
once(_event, _callback, _source): this {
|
||||||
// return this.phaserText.once(event, callback, source);
|
// return this.phaserText.once(event, callback, source);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
off(_event, _callback, _obj) {}
|
off(_event, _callback, _obj) {}
|
||||||
|
|
||||||
removedFromScene() {}
|
removedFromScene() {}
|
||||||
|
|
||||||
addToDisplayList() {}
|
addToDisplayList(): this {
|
||||||
|
return this;
|
||||||
setStroke(_color, _thickness) {
|
|
||||||
// Sets the stroke color and thickness.
|
|
||||||
// return this.phaserText.setStroke(color, thickness);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromDisplayList() {
|
setStroke(_color, _thickness): this {
|
||||||
|
// Sets the stroke color and thickness.
|
||||||
|
// return this.phaserText.setStroke(color, thickness);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFromDisplayList(): this {
|
||||||
// same as remove or destroy
|
// same as remove or destroy
|
||||||
// return this.phaserText.removeFromDisplayList();
|
// return this.phaserText.removeFromDisplayList();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addedToScene() {
|
addedToScene() {
|
||||||
@ -150,16 +159,18 @@ export default class MockText implements MockGameObject {
|
|||||||
// return this.phaserText.addedToScene();
|
// return this.phaserText.addedToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisible(_visible) {
|
setVisible(_visible): this {
|
||||||
// return this.phaserText.setVisible(visible);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setY(_y) {
|
setY(_y): this {
|
||||||
// return this.phaserText.setY(y);
|
// return this.phaserText.setY(y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setX(_x) {
|
setX(_x): this {
|
||||||
// return this.phaserText.setX(x);
|
// return this.phaserText.setX(x);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,37 +180,45 @@ export default class MockText implements MockGameObject {
|
|||||||
* @param z The z position of this Game Object. Default 0.
|
* @param z The z position of this Game Object. Default 0.
|
||||||
* @param w The w position of this Game Object. Default 0.
|
* @param w The w position of this Game Object. Default 0.
|
||||||
*/
|
*/
|
||||||
setPosition(_x?: number, _y?: number, _z?: number, _w?: number) {}
|
setPosition(_x?: number, _y?: number, _z?: number, _w?: number): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setText(text) {
|
setText(text): this {
|
||||||
// Sets the text this Game Object will display.
|
// Sets the text this Game Object will display.
|
||||||
// return this.phaserText.setText\(text);
|
// return this.phaserText.setText\(text);
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAngle(_angle) {
|
setAngle(_angle): this {
|
||||||
// Sets the angle of this Game Object.
|
// Sets the angle of this Game Object.
|
||||||
// return this.phaserText.setAngle(angle);
|
// return this.phaserText.setAngle(angle);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPositionRelative(_source, _x, _y) {
|
setPositionRelative(_source, _x, _y): this {
|
||||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||||
// return this.phaserText.setPositionRelative(source, x, y);
|
// return this.phaserText.setPositionRelative(source, x, y);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setShadowOffset(_offsetX, _offsetY) {
|
setShadowOffset(_offsetX, _offsetY): this {
|
||||||
// Sets the shadow offset values.
|
// Sets the shadow offset values.
|
||||||
// return this.phaserText.setShadowOffset(offsetX, offsetY);
|
// return this.phaserText.setShadowOffset(offsetX, offsetY);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWordWrapWidth(width) {
|
setWordWrapWidth(width): this {
|
||||||
// Sets the width (in pixels) to use for wrapping lines.
|
// Sets the width (in pixels) to use for wrapping lines.
|
||||||
this.wordWrapWidth = width;
|
this.wordWrapWidth = width;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFontSize(_fontSize) {
|
setFontSize(_fontSize): this {
|
||||||
// Sets the font size of this Game Object.
|
// Sets the font size of this Game Object.
|
||||||
// return this.phaserText.setFontSize(fontSize);
|
// return this.phaserText.setFontSize(fontSize);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBounds() {
|
getBounds() {
|
||||||
@ -209,25 +228,31 @@ export default class MockText implements MockGameObject {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setColor(color: string) {
|
setColor(color: string): this {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setInteractive = () => null;
|
setInteractive(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setShadowColor(_color) {
|
setShadowColor(_color): this {
|
||||||
// Sets the shadow color.
|
// Sets the shadow color.
|
||||||
// return this.phaserText.setShadowColor(color);
|
// return this.phaserText.setShadowColor(color);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTint(_color) {
|
setTint(_color): this {
|
||||||
// Sets the tint of this Game Object.
|
// Sets the tint of this Game Object.
|
||||||
// return this.phaserText.setTint(color);
|
// return this.phaserText.setTint(color);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStrokeStyle(_thickness, _color) {
|
setStrokeStyle(_thickness, _color): this {
|
||||||
// Sets the stroke style for the graphics.
|
// Sets the stroke style for the graphics.
|
||||||
// return this.phaserText.setStrokeStyle(thickness, color);
|
// return this.phaserText.setStrokeStyle(thickness, color);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
@ -235,20 +260,24 @@ export default class MockText implements MockGameObject {
|
|||||||
this.list = [];
|
this.list = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
setAlpha(_alpha) {
|
setAlpha(_alpha): this {
|
||||||
// return this.phaserText.setAlpha(alpha);
|
// return this.phaserText.setAlpha(alpha);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(name: string) {
|
setName(name: string): this {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAlign(_align) {
|
setAlign(_align): this {
|
||||||
// return this.phaserText.setAlign(align);
|
// return this.phaserText.setAlign(align);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMask() {
|
setMask(): this {
|
||||||
/// Sets the mask that this Game Object will use to render with.
|
/// Sets the mask that this Game Object will use to render with.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBottomLeft() {
|
getBottomLeft() {
|
||||||
@ -265,37 +294,43 @@ export default class MockText implements MockGameObject {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
disableInteractive() {
|
disableInteractive(): this {
|
||||||
// Disables interaction with this Game Object.
|
// Disables interaction with this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTint() {
|
clearTint(): this {
|
||||||
// Clears tint on this Game Object.
|
// Clears tint on this Game Object.
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(obj) {
|
add(obj): this {
|
||||||
// Adds a child to this Game Object.
|
// Adds a child to this Game Object.
|
||||||
this.list.push(obj);
|
this.list.push(obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll() {
|
removeAll(): this {
|
||||||
// Removes all Game Objects from this Container.
|
// Removes all Game Objects from this Container.
|
||||||
this.list = [];
|
this.list = [];
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addAt(obj, index) {
|
addAt(obj, index): this {
|
||||||
// Adds a Game Object to this Container at the given index.
|
// Adds a Game Object to this Container at the given index.
|
||||||
this.list.splice(index, 0, obj);
|
this.list.splice(index, 0, obj);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(obj) {
|
remove(obj): this {
|
||||||
const index = this.list.indexOf(obj);
|
const index = this.list.indexOf(obj);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.list.splice(index, 1);
|
this.list.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndex(obj) {
|
getIndex(obj): number {
|
||||||
const index = this.list.indexOf(obj);
|
const index = this.list.indexOf(obj);
|
||||||
return index || -1;
|
return index || -1;
|
||||||
}
|
}
|
||||||
@ -317,5 +352,6 @@ export default class MockText implements MockGameObject {
|
|||||||
return this.runWordWrap(this.text).split("\n");
|
return this.runWordWrap(this.text).split("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noBannedTypes: This matches the signature of the class this mocks
|
||||||
on(_event: string | symbol, _fn: Function, _context?: any) {}
|
on(_event: string | symbol, _fn: Function, _context?: any) {}
|
||||||
}
|
}
|
||||||
|
@ -70,10 +70,10 @@ export function initTestFile() {
|
|||||||
* @param x The relative x position
|
* @param x The relative x position
|
||||||
* @param y The relative y position
|
* @param y The relative y position
|
||||||
*/
|
*/
|
||||||
const setPositionRelative = function (guideObject: any, x: number, y: number) {
|
const setPositionRelative = function (guideObject: any, x: number, y: number): any {
|
||||||
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
||||||
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
||||||
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
return this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
||||||
};
|
};
|
||||||
|
|
||||||
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
||||||
|