mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-01 05:52:17 +02:00
Add setting for showing type effectiveness hints (#1061)
* type hints * fix overwritten change * don't set color to white, just leave it unchanged * remove unrelated code * don't show hints if no opponents, use type effectiveness instead of move effectiveness * fix color not going back to white when new opponent is sent * move effectiveness to move info container * add effectiveness overlay, partial hints only show move effectiveness, improve colors * lint * docs * remove full hints, move container to right of enemy info box * hide effectiveness while flyout is visible * move setting to display, use default style color instead of white
This commit is contained in:
parent
5e5ece868c
commit
3f9eaf4a5d
@ -155,6 +155,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
*/
|
*/
|
||||||
public battleStyle: integer = 0;
|
public battleStyle: integer = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether or not to show type effectiveness hints
|
||||||
|
* - true: No hints
|
||||||
|
* - false: Show hints for moves
|
||||||
|
*/
|
||||||
|
public typeHints: boolean = false;
|
||||||
|
|
||||||
public disableMenu: boolean = false;
|
public disableMenu: boolean = false;
|
||||||
|
|
||||||
public gameData: GameData;
|
public gameData: GameData;
|
||||||
|
@ -501,6 +501,52 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the color corresponding to a specific damage multiplier
|
||||||
|
* @returns A color or undefined if the default color should be used
|
||||||
|
*/
|
||||||
|
export function getTypeDamageMultiplierColor(multiplier: TypeDamageMultiplier, side: "defense" | "offense"): string | undefined {
|
||||||
|
if (side === "offense") {
|
||||||
|
switch (multiplier) {
|
||||||
|
case 0:
|
||||||
|
return "#929292";
|
||||||
|
case 0.125:
|
||||||
|
return "#FF5500";
|
||||||
|
case 0.25:
|
||||||
|
return "#FF7400";
|
||||||
|
case 0.5:
|
||||||
|
return "#FE8E00";
|
||||||
|
case 1:
|
||||||
|
return undefined;
|
||||||
|
case 2:
|
||||||
|
return "#4AA500";
|
||||||
|
case 4:
|
||||||
|
return "#4BB400";
|
||||||
|
case 8:
|
||||||
|
return "#52C200";
|
||||||
|
}
|
||||||
|
} else if (side === "defense") {
|
||||||
|
switch (multiplier) {
|
||||||
|
case 0:
|
||||||
|
return "#B1B100";
|
||||||
|
case 0.125:
|
||||||
|
return "#2DB4FF";
|
||||||
|
case 0.25:
|
||||||
|
return "#00A4FF";
|
||||||
|
case 0.5:
|
||||||
|
return "#0093FF";
|
||||||
|
case 1:
|
||||||
|
return undefined;
|
||||||
|
case 2:
|
||||||
|
return "#FE8E00";
|
||||||
|
case 4:
|
||||||
|
return "#FF7400";
|
||||||
|
case 8:
|
||||||
|
return "#FF5500";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getTypeRgb(type: Type): [ integer, integer, integer ] {
|
export function getTypeRgb(type: Type): [ integer, integer, integer ] {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type.NORMAL:
|
case Type.NORMAL:
|
||||||
|
@ -1069,6 +1069,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return !this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE);
|
return !this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The type damage multiplier or undefined if it's a status move
|
||||||
|
*/
|
||||||
|
getMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier | undefined {
|
||||||
|
if (move.getMove().category === MoveCategory.STATUS) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getAttackMoveEffectiveness(source, move);
|
||||||
|
}
|
||||||
|
|
||||||
getAttackMoveEffectiveness(source: Pokemon, pokemonMove: PokemonMove): TypeDamageMultiplier {
|
getAttackMoveEffectiveness(source: Pokemon, pokemonMove: PokemonMove): TypeDamageMultiplier {
|
||||||
const move = pokemonMove.getMove();
|
const move = pokemonMove.getMove();
|
||||||
const typeless = move.hasAttr(TypelessAttr);
|
const typeless = move.hasAttr(TypelessAttr);
|
||||||
@ -1588,11 +1599,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.battleInfo.updateInfo(this, instant);
|
return this.battleInfo.updateInfo(this, instant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show or hide the type effectiveness multiplier window
|
||||||
|
* Passing undefined will hide the window
|
||||||
|
*/
|
||||||
|
updateEffectiveness(effectiveness?: string) {
|
||||||
|
this.battleInfo.updateEffectiveness(effectiveness);
|
||||||
|
}
|
||||||
|
|
||||||
toggleStats(visible: boolean): void {
|
toggleStats(visible: boolean): void {
|
||||||
this.battleInfo.toggleStats(visible);
|
this.battleInfo.toggleStats(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFlyout(visible: boolean): void {
|
toggleFlyout(visible: boolean): void {
|
||||||
this.battleInfo.flyoutMenu?.toggleFlyout(visible);
|
this.battleInfo.toggleFlyout(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
addExp(exp: integer) {
|
addExp(exp: integer) {
|
||||||
|
@ -64,6 +64,7 @@ export const SettingKeys = {
|
|||||||
Sprite_Set: "SPRITE_SET",
|
Sprite_Set: "SPRITE_SET",
|
||||||
Fusion_Palette_Swaps: "FUSION_PALETTE_SWAPS",
|
Fusion_Palette_Swaps: "FUSION_PALETTE_SWAPS",
|
||||||
Player_Gender: "PLAYER_GENDER",
|
Player_Gender: "PLAYER_GENDER",
|
||||||
|
Type_Hints: "TYPE_HINTS",
|
||||||
Master_Volume: "MASTER_VOLUME",
|
Master_Volume: "MASTER_VOLUME",
|
||||||
BGM_Volume: "BGM_VOLUME",
|
BGM_Volume: "BGM_VOLUME",
|
||||||
SE_Volume: "SE_VOLUME",
|
SE_Volume: "SE_VOLUME",
|
||||||
@ -268,6 +269,13 @@ export const Setting: Array<Setting> = [
|
|||||||
default: 0,
|
default: 0,
|
||||||
type: SettingType.DISPLAY
|
type: SettingType.DISPLAY
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Type_Hints,
|
||||||
|
label: "Type hints",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.DISPLAY
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: SettingKeys.Master_Volume,
|
key: SettingKeys.Master_Volume,
|
||||||
label: "Master Volume",
|
label: "Master Volume",
|
||||||
@ -447,6 +455,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
|
|||||||
case SettingKeys.Vibration:
|
case SettingKeys.Vibration:
|
||||||
scene.enableVibration = Setting[index].options[value] !== "Disabled" && hasTouchscreen();
|
scene.enableVibration = Setting[index].options[value] !== "Disabled" && hasTouchscreen();
|
||||||
break;
|
break;
|
||||||
|
case SettingKeys.Type_Hints:
|
||||||
|
scene.typeHints = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
case SettingKeys.Language:
|
case SettingKeys.Language:
|
||||||
if (value) {
|
if (value) {
|
||||||
if (scene.ui) {
|
if (scene.ui) {
|
||||||
|
@ -55,6 +55,9 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
|
|||||||
/** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */
|
/** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */
|
||||||
private moveInfo: MoveInfo[] = new Array();
|
private moveInfo: MoveInfo[] = new Array();
|
||||||
|
|
||||||
|
/** Current state of the flyout's visibility */
|
||||||
|
public flyoutVisible: boolean = false;
|
||||||
|
|
||||||
// Stores callbacks in a variable so they can be unsubscribed from when destroyed
|
// Stores callbacks in a variable so they can be unsubscribed from when destroyed
|
||||||
private readonly onMoveUsedEvent = (event: Event) => this.onMoveUsed(event);
|
private readonly onMoveUsedEvent = (event: Event) => this.onMoveUsed(event);
|
||||||
private readonly onBerryUsedEvent = (event: Event) => this.onBerryUsed(event);
|
private readonly onBerryUsedEvent = (event: Event) => this.onBerryUsed(event);
|
||||||
@ -170,6 +173,8 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/** Animates the flyout to either show or hide it by applying a fade and translation */
|
/** Animates the flyout to either show or hide it by applying a fade and translation */
|
||||||
toggleFlyout(visible: boolean): void {
|
toggleFlyout(visible: boolean): void {
|
||||||
|
this.flyoutVisible = visible;
|
||||||
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this.flyoutParent,
|
targets: this.flyoutParent,
|
||||||
x: visible ? this.anchorX : this.anchorX - this.translationX,
|
x: visible ? this.anchorX : this.anchorX - this.translationX,
|
||||||
|
@ -9,6 +9,7 @@ import { Type, getTypeRgb } from "../data/type";
|
|||||||
import { getVariantTint } from "#app/data/variant";
|
import { getVariantTint } from "#app/data/variant";
|
||||||
import { BattleStat } from "#app/data/battle-stat";
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
import BattleFlyout from "./battle-flyout";
|
import BattleFlyout from "./battle-flyout";
|
||||||
|
import { WindowVariant, addWindow } from "./ui-theme";
|
||||||
|
|
||||||
const battleStatOrder = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD ];
|
const battleStatOrder = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD ];
|
||||||
|
|
||||||
@ -52,6 +53,13 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
|||||||
private type3Icon: Phaser.GameObjects.Sprite;
|
private type3Icon: Phaser.GameObjects.Sprite;
|
||||||
private expBar: Phaser.GameObjects.Image;
|
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;
|
public expMaskRect: Phaser.GameObjects.Graphics;
|
||||||
|
|
||||||
private statsContainer: Phaser.GameObjects.Container;
|
private statsContainer: Phaser.GameObjects.Container;
|
||||||
@ -59,7 +67,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
|||||||
private statValuesContainer: Phaser.GameObjects.Container;
|
private statValuesContainer: Phaser.GameObjects.Container;
|
||||||
private statNumbers: Phaser.GameObjects.Sprite[];
|
private statNumbers: Phaser.GameObjects.Sprite[];
|
||||||
|
|
||||||
public flyoutMenu: BattleFlyout;
|
public flyoutMenu?: BattleFlyout;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) {
|
constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) {
|
||||||
super(scene, x, y);
|
super(scene, x, y);
|
||||||
@ -250,6 +258,19 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
|||||||
this.type3Icon.setName("icon_type_3");
|
this.type3Icon.setName("icon_type_3");
|
||||||
this.type3Icon.setOrigin(0, 0);
|
this.type3Icon.setOrigin(0, 0);
|
||||||
this.add(this.type3Icon);
|
this.add(this.type3Icon);
|
||||||
|
|
||||||
|
if (!this.player) {
|
||||||
|
this.effectivenessContainer = this.scene.add.container(0, 0);
|
||||||
|
this.effectivenessContainer.setPositionRelative(this.type1Icon, 22, 4);
|
||||||
|
this.effectivenessContainer.setVisible(false);
|
||||||
|
this.add(this.effectivenessContainer);
|
||||||
|
|
||||||
|
this.effectivenessText = addTextObject(this.scene, 5, 4.5, "", TextStyle.BATTLE_INFO);
|
||||||
|
this.effectivenessWindow = addWindow((this.scene as BattleScene), 0, 0, 0, 20, false, false, null, null, WindowVariant.XTHIN);
|
||||||
|
|
||||||
|
this.effectivenessContainer.add(this.effectivenessWindow);
|
||||||
|
this.effectivenessContainer.add(this.effectivenessText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initInfo(pokemon: Pokemon) {
|
initInfo(pokemon: Pokemon) {
|
||||||
@ -711,6 +732,39 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 (!(this.scene as BattleScene).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 {
|
getBaseY(): number {
|
||||||
return this.baseY;
|
return this.baseY;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import { addTextObject, TextStyle } from "./text";
|
import { addTextObject, TextStyle } from "./text";
|
||||||
import { Type } from "../data/type";
|
import { getTypeDamageMultiplierColor, Type } from "../data/type";
|
||||||
import { Command } from "./command-ui-handler";
|
import { Command } from "./command-ui-handler";
|
||||||
import { Mode } from "./ui";
|
import { Mode } from "./ui";
|
||||||
import UiHandler from "./ui-handler";
|
import UiHandler from "./ui-handler";
|
||||||
@ -9,6 +9,7 @@ import { CommandPhase } from "../phases";
|
|||||||
import { MoveCategory } from "#app/data/move.js";
|
import { MoveCategory } from "#app/data/move.js";
|
||||||
import i18next from "../plugins/i18n";
|
import i18next from "../plugins/i18n";
|
||||||
import {Button} from "../enums/buttons";
|
import {Button} from "../enums/buttons";
|
||||||
|
import Pokemon, { PokemonMove } from "#app/field/pokemon.js";
|
||||||
|
|
||||||
export default class FightUiHandler extends UiHandler {
|
export default class FightUiHandler extends UiHandler {
|
||||||
private movesContainer: Phaser.GameObjects.Container;
|
private movesContainer: Phaser.GameObjects.Container;
|
||||||
@ -162,7 +163,8 @@ export default class FightUiHandler extends UiHandler {
|
|||||||
ui.add(this.cursorObj);
|
ui.add(this.cursorObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
const moveset = (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getMoveset();
|
const pokemon = (this.scene.getCurrentPhase() as CommandPhase).getPokemon();
|
||||||
|
const moveset = pokemon.getMoveset();
|
||||||
|
|
||||||
const hasMove = cursor < moveset.length;
|
const hasMove = cursor < moveset.length;
|
||||||
|
|
||||||
@ -179,6 +181,10 @@ export default class FightUiHandler extends UiHandler {
|
|||||||
this.ppText.setText(`${Utils.padInt(pp, 2, " ")}/${Utils.padInt(maxPP, 2, " ")}`);
|
this.ppText.setText(`${Utils.padInt(pp, 2, " ")}/${Utils.padInt(maxPP, 2, " ")}`);
|
||||||
this.powerText.setText(`${power >= 0 ? power : "---"}`);
|
this.powerText.setText(`${power >= 0 ? power : "---"}`);
|
||||||
this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`);
|
this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`);
|
||||||
|
|
||||||
|
pokemon.getOpponents().forEach((opponent) => {
|
||||||
|
opponent.updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.typeIcon.setVisible(hasMove);
|
this.typeIcon.setVisible(hasMove);
|
||||||
@ -195,17 +201,60 @@ export default class FightUiHandler extends UiHandler {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
displayMoves() {
|
/**
|
||||||
const moveset = (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getMoveset();
|
* Gets multiplier text for a pokemon's move against a specific opponent
|
||||||
for (let m = 0; m < 4; m++) {
|
* Returns undefined if it's a status move
|
||||||
const moveText = addTextObject(this.scene, m % 2 === 0 ? 0 : 100, m < 2 ? 0 : 16, "-", TextStyle.WINDOW);
|
*/
|
||||||
if (m < moveset.length) {
|
private getEffectivenessText(pokemon: Pokemon, opponent: Pokemon, pokemonMove: PokemonMove): string | undefined {
|
||||||
moveText.setText(moveset[m].getName());
|
const effectiveness = opponent.getMoveEffectiveness(pokemon, pokemonMove);
|
||||||
|
if (effectiveness === undefined) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return `${effectiveness}x`;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayMoves() {
|
||||||
|
const pokemon = (this.scene.getCurrentPhase() as CommandPhase).getPokemon();
|
||||||
|
const moveset = pokemon.getMoveset();
|
||||||
|
|
||||||
|
for (let moveIndex = 0; moveIndex < 4; moveIndex++) {
|
||||||
|
const moveText = addTextObject(this.scene, moveIndex % 2 === 0 ? 0 : 100, moveIndex < 2 ? 0 : 16, "-", TextStyle.WINDOW);
|
||||||
|
|
||||||
|
if (moveIndex < moveset.length) {
|
||||||
|
const pokemonMove = moveset[moveIndex];
|
||||||
|
moveText.setText(pokemonMove.getName());
|
||||||
|
moveText.setColor(this.getMoveColor(pokemon, pokemonMove) ?? moveText.style.color);
|
||||||
|
}
|
||||||
|
|
||||||
this.movesContainer.add(moveText);
|
this.movesContainer.add(moveText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a specific move's color based on its type effectiveness against opponents
|
||||||
|
* If there are multiple opponents, the highest effectiveness' color is returned
|
||||||
|
* @returns A color or undefined if the default color should be used
|
||||||
|
*/
|
||||||
|
private getMoveColor(pokemon: Pokemon, pokemonMove: PokemonMove): string | undefined {
|
||||||
|
if (!this.scene.typeHints) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const opponents = pokemon.getOpponents();
|
||||||
|
if (opponents.length <= 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveColors = opponents.map((opponent) => {
|
||||||
|
return opponent.getMoveEffectiveness(pokemon, pokemonMove);
|
||||||
|
}).sort((a, b) => b - a).map((effectiveness) => {
|
||||||
|
return getTypeDamageMultiplierColor(effectiveness, "offense");
|
||||||
|
});
|
||||||
|
|
||||||
|
return moveColors[0];
|
||||||
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
this.clearMoves();
|
this.clearMoves();
|
||||||
@ -222,6 +271,11 @@ export default class FightUiHandler extends UiHandler {
|
|||||||
|
|
||||||
clearMoves() {
|
clearMoves() {
|
||||||
this.movesContainer.removeAll(true);
|
this.movesContainer.removeAll(true);
|
||||||
|
|
||||||
|
const opponents = (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents();
|
||||||
|
opponents.forEach((opponent) => {
|
||||||
|
opponent.updateEffectiveness(undefined);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
eraseCursor() {
|
eraseCursor() {
|
||||||
|
Loading…
Reference in New Issue
Block a user