Merge branch 'beta' into substitute

This commit is contained in:
innerthunder 2024-09-09 13:04:16 -07:00
commit a4cfd7b0b8
14 changed files with 316 additions and 37 deletions

View File

@ -1595,8 +1595,8 @@ export class PostAttackAbAttr extends AbAttr {
private attackCondition: PokemonAttackCondition;
/** The default attackCondition requires that the selected move is a damaging move */
constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS)) {
super();
constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS), showAbility: boolean = true) {
super(showAbility);
this.attackCondition = attackCondition;
}
@ -1624,6 +1624,40 @@ export class PostAttackAbAttr extends AbAttr {
}
}
/**
* Ability attribute for Gorilla Tactics
* @extends PostAttackAbAttr
*/
export class GorillaTacticsAbAttr extends PostAttackAbAttr {
constructor() {
super((user, target, move) => true, false);
}
/**
*
* @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability
* @param passive n/a
* @param simulated whether the ability is being simulated
* @param defender n/a
* @param move n/a
* @param hitResult n/a
* @param args n/a
* @returns `true` if the ability is applied
*/
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise<boolean> {
if (simulated) {
return simulated;
}
if (pokemon.getTag(BattlerTagType.GORILLA_TACTICS)) {
return false;
}
pokemon.addTag(BattlerTagType.GORILLA_TACTICS);
return true;
}
}
export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
private stealCondition: PokemonAttackCondition | null;
@ -5604,7 +5638,7 @@ export function initAbilities() {
.bypassFaint()
.partial(),
new Ability(Abilities.GORILLA_TACTICS, 8)
.unimplemented(),
.attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8)
.attr(SuppressFieldAbilitiesAbAttr)
.attr(UncopiableAbilityAbAttr)

View File

@ -121,7 +121,9 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
const move = phase.move;
if (this.isMoveRestricted(move.moveId)) {
if (this.interruptedText(pokemon, move.moveId)) {
pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId));
}
phase.cancel();
}
@ -157,7 +159,9 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
* @param {Moves} move {@linkcode Moves} ID of the move being interrupted
* @returns {string} text to display when the move is interrupted
*/
abstract interruptedText(pokemon: Pokemon, move: Moves): string;
interruptedText(pokemon: Pokemon, move: Moves): string {
return "";
}
}
/**
@ -223,7 +227,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
/**
* @override
*
* Ensures that move history exists on `pokemon` and has a valid move. If so, sets the {@link moveId} and shows a message.
* Ensures that move history exists on `pokemon` and has a valid move. If so, sets the {@linkcode moveId} and shows a message.
* Otherwise the move ID will not get assigned and this tag will get removed next turn.
*/
override onAdd(pokemon: Pokemon): void {
@ -252,7 +256,12 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
return i18next.t("battle:moveDisabled", { moveName: allMoves[move].name });
}
/** @override */
/**
* @override
* @param {Pokemon} pokemon {@linkcode Pokemon} attempting to use the restricted move
* @param {Moves} move {@linkcode Moves} ID of the move being interrupted
* @returns {string} text to display when the move is interrupted
*/
override interruptedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
}
@ -264,6 +273,72 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
}
}
/**
* Tag used by Gorilla Tactics to restrict the user to using only one move.
* @extends MoveRestrictionBattlerTag
*/
export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
private moveId = Moves.NONE;
constructor() {
super(BattlerTagType.GORILLA_TACTICS, BattlerTagLapseType.CUSTOM, 0);
}
/** @override */
override isMoveRestricted(move: Moves): boolean {
return move !== this.moveId;
}
/**
* @override
* @param {Pokemon} pokemon the {@linkcode Pokemon} to check if the tag can be added
* @returns `true` if the pokemon has a valid move and no existing {@linkcode GorillaTacticsTag}; `false` otherwise
*/
override canAdd(pokemon: Pokemon): boolean {
return (this.getLastValidMove(pokemon) !== undefined) && !pokemon.getTag(GorillaTacticsTag);
}
/**
* Ensures that move history exists on {@linkcode Pokemon} and has a valid move.
* If so, sets the {@linkcode moveId} and increases the user's Attack by 50%.
* @override
* @param {Pokemon} pokemon the {@linkcode Pokemon} to add the tag to
*/
override onAdd(pokemon: Pokemon): void {
const lastValidMove = this.getLastValidMove(pokemon);
if (!lastValidMove) {
return;
}
this.moveId = lastValidMove;
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
}
/**
*
* @override
* @param {Pokemon} pokemon n/a
* @param {Moves} move {@linkcode Moves} ID of the move being denied
* @returns {string} text to display when the move is denied
*/
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
return i18next.t("battle:canOnlyUseMove", { moveName: allMoves[this.moveId].name, pokemonName: getPokemonNameWithAffix(pokemon) });
}
/**
* Gets the last valid move from the pokemon's move history.
* @param {Pokemon} pokemon {@linkcode Pokemon} to get the last valid move from
* @returns {Moves | undefined} the last valid move from the pokemon's move history
*/
getLastValidMove(pokemon: Pokemon): Moves | undefined {
const move = pokemon.getLastXMoves()
.find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual);
return move?.move;
}
}
/**
* BattlerTag that represents the "recharge" effects of moves like Hyper Beam.
*/
@ -2296,6 +2371,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new TarShotTag();
case BattlerTagType.THROAT_CHOPPED:
return new ThroatChoppedTag();
case BattlerTagType.GORILLA_TACTICS:
return new GorillaTacticsTag();
case BattlerTagType.SUBSTITUTE:
return new SubstituteTag(sourceMove, sourceId);
case BattlerTagType.NONE:

View File

@ -74,6 +74,7 @@ export enum BattlerTagType {
SHELL_TRAP = "SHELL_TRAP",
DRAGON_CHEER = "DRAGON_CHEER",
NO_RETREAT = "NO_RETREAT",
GORILLA_TACTICS = "GORILLA_TACTICS",
THROAT_CHOPPED = "THROAT_CHOPPED",
TAR_SHOT = "TAR_SHOT",
}

View File

@ -3,5 +3,6 @@
"power": "Stärke",
"accuracy": "Genauigkeit",
"abilityFlyInText": "{{passive}}{{abilityName}} von {{pokemonName}} wirkt!",
"passive": "Passive Fähigkeit "
"passive": "Passive Fähigkeit ",
"teraHover": "Tera-Typ {{type}}"
}

View File

@ -44,6 +44,7 @@
"moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.",
"moveNoPP": "There's no PP left for\nthis move!",
"moveDisabled": "{{moveName}} is disabled!",
"canOnlyUseMove": "{{pokemonName}} can only use {{moveName}}!",
"moveCannotBeSelected": "{{moveName}} cannot be selected!",
"disableInterruptedMove": "{{pokemonNameWithAffix}}'s {{moveName}}\nis disabled!",
"throatChopInterruptedMove": "The effects of Throat Chop prevent\n{{pokemonName}} from using certain moves!",

View File

@ -3,5 +3,6 @@
"power": "Power",
"accuracy": "Accuracy",
"abilityFlyInText": " {{pokemonName}}'s {{passive}}{{abilityName}}",
"passive": "Passive "
"passive": "Passive ",
"teraHover": "{{type}} Terastallized"
}

View File

@ -0,0 +1,83 @@
import { BattlerIndex } from "#app/battle";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { Stat } from "#app/enums/stat";
import { Abilities } from "#enums/abilities";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Gorilla Tactics", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const TIMEOUT = 20 * 1000;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset([Moves.SPLASH, Moves.DISABLE])
.enemySpecies(Species.MAGIKARP)
.enemyLevel(30)
.moveset([Moves.SPLASH, Moves.TACKLE, Moves.GROWL])
.ability(Abilities.GORILLA_TACTICS);
});
it("boosts the Pokémon's Attack by 50%, but limits the Pokémon to using only one move", async () => {
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const initialAtkStat = darmanitan.getStat(Stat.ATK);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(darmanitan.getStat(Stat.ATK, false)).toBeCloseTo(initialAtkStat * 1.5);
// Other moves should be restricted
expect(darmanitan.isMoveRestricted(Moves.TACKLE)).toBe(true);
expect(darmanitan.isMoveRestricted(Moves.SPLASH)).toBe(false);
}, TIMEOUT);
it("should struggle if the only usable move is disabled", async () => {
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
// First turn, lock move to Growl
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.SPLASH);
// Second turn, Growl is interrupted by Disable
await game.toNextTurn();
game.move.select(Moves.GROWL);
await game.forceEnemyMove(Moves.DISABLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.getStatStage(Stat.ATK)).toBe(-1); // Only the effect of the first Growl should be applied
// Third turn, Struggle is used
await game.toNextTurn();
game.move.select(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEndPhase");
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
}, TIMEOUT);
});

View File

@ -36,12 +36,14 @@ describe("Moves - Throat Chop", () => {
it("prevents the target from using sound-based moves for two turns", async () => {
await game.classicMode.startBattle([Species.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
game.move.select(Moves.GROWL);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
// First turn, move is interrupted
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
// Second turn, struggle if no valid moves
await game.toNextTurn();
@ -50,6 +52,6 @@ describe("Moves - Throat Chop", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEndPhase");
expect(game.scene.getEnemyPokemon()!.isFullHp()).toBe(false);
expect(enemy.isFullHp()).toBe(false);
}, TIMEOUT);
});

View File

@ -52,9 +52,8 @@ export default class MockContainer implements MockGameObject {
/// Sets the position of this Game Object to be a relative position from the source Game Object.
}
setInteractive(hitArea?, callback?, dropZone?) {
/// Sets the InteractiveObject to be a drop zone for a drag and drop operation.
}
setInteractive = vi.fn();
setOrigin(x, y) {
this.x = x;
this.y = y;

View File

@ -1,5 +1,6 @@
import Phaser from "phaser";
import { MockGameObject } from "../mockGameObject";
import { vi } from "vitest";
import Sprite = Phaser.GameObjects.Sprite;
import Frame = Phaser.Textures.Frame;
@ -101,9 +102,7 @@ export default class MockSprite implements MockGameObject {
return this.phaserSprite.stop();
}
setInteractive(hitArea, hitAreaCallback, dropZone) {
return null;
}
setInteractive = vi.fn();
on(event, callback, source) {
return this.phaserSprite.on(event, callback, source);

View File

@ -197,6 +197,8 @@ export default class MockText implements MockGameObject {
this.color = color;
});
setInteractive = vi.fn();
setShadowColor(color) {
// Sets the shadow color.
// return this.phaserText.setShadowColor(color);

View File

@ -323,7 +323,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.teraIcon.setVisible(this.lastTeraType !== Type.UNKNOWN);
this.teraIcon.on("pointerover", () => {
if (this.lastTeraType !== Type.UNKNOWN) {
(this.scene as BattleScene).ui.showTooltip("", `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`);
(this.scene as BattleScene).ui.showTooltip("", i18next.t("fightUiHandler:teraHover", {type: i18next.t(`pokemonInfo:Type.${Type[this.lastTeraType]}`) }));
}
});
this.teraIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip());

View File

@ -266,6 +266,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private pokemonPassiveDisabledIcon: Phaser.GameObjects.Sprite;
private pokemonPassiveLockedIcon: Phaser.GameObjects.Sprite;
private activeTooltip: "ABILITY" | "PASSIVE" | "CANDY" | undefined;
private instructionsContainer: Phaser.GameObjects.Container;
private filterInstructionsContainer: Phaser.GameObjects.Container;
private shinyIconElement: Phaser.GameObjects.Sprite;
@ -561,10 +562,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 127 + starterInfoYOffset, i18next.t("starterSelectUiHandler:ability"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize });
this.pokemonAbilityLabelText.setOrigin(0, 0);
this.pokemonAbilityLabelText.setVisible(false);
this.starterSelectContainer.add(this.pokemonAbilityLabelText);
this.pokemonAbilityText = addTextObject(this.scene, starterInfoXPos, 127 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize });
this.pokemonAbilityText.setOrigin(0, 0);
this.pokemonAbilityText.setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains);
this.starterSelectContainer.add(this.pokemonAbilityText);
this.pokemonPassiveLabelText = addTextObject(this.scene, 6, 136 + starterInfoYOffset, i18next.t("starterSelectUiHandler:passive"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize });
@ -574,6 +578,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonPassiveText = addTextObject(this.scene, starterInfoXPos, 136 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize });
this.pokemonPassiveText.setOrigin(0, 0);
this.pokemonPassiveText.setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains);
this.starterSelectContainer.add(this.pokemonPassiveText);
this.pokemonPassiveDisabledIcon = this.scene.add.sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_stop");
@ -1921,6 +1926,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
}
} while (newAbilityIndex !== this.abilityCursor);
starterAttributes.ability = newAbilityIndex; // store the selected ability
const { visible: tooltipVisible } = this.scene.ui.getTooltip();
if (tooltipVisible && this.activeTooltip === "ABILITY") {
const newAbility = allAbilities[this.lastSpecies.getAbility(newAbilityIndex)];
this.scene.ui.editTooltip(`${newAbility.name}`, `${newAbility.description}`);
}
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, newAbilityIndex, undefined);
success = true;
}
@ -2687,12 +2700,30 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
}
}
getFriendship(speciesId: number) {
let currentFriendship = this.scene.gameData.starterData[speciesId].friendship;
if (!currentFriendship || currentFriendship === undefined) {
currentFriendship = 0;
}
const friendshipCap = getStarterValueFriendshipCap(speciesStarters[speciesId]);
return { currentFriendship, friendshipCap };
}
setSpecies(species: PokemonSpecies | null) {
this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null;
this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n;
this.abilityCursor = species ? this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0;
this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0;
if (!species && this.scene.ui.getTooltip().visible) {
this.scene.ui.hideTooltip();
}
this.pokemonAbilityText.off("pointerover");
this.pokemonPassiveText.off("pointerover");
const starterAttributes : StarterAttributes | null = species ? {...this.starterPreferences[species.speciesId]} : null;
if (starterAttributes?.nature) {
@ -2807,17 +2838,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonHatchedIcon.setVisible(true);
this.pokemonHatchedCountText.setVisible(true);
let currentFriendship = this.scene.gameData.starterData[this.lastSpecies.speciesId].friendship;
if (!currentFriendship || currentFriendship === undefined) {
currentFriendship = 0;
}
const friendshipCap = getStarterValueFriendshipCap(speciesStarters[this.lastSpecies.speciesId]);
const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId);
const candyCropY = 16 - (16 * (currentFriendship / friendshipCap));
if (this.pokemonCandyDarknessOverlay.visible) {
this.pokemonCandyDarknessOverlay.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true));
this.pokemonCandyDarknessOverlay.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip());
this.pokemonCandyDarknessOverlay.on("pointerover", () => {
this.scene.ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true);
this.activeTooltip = "CANDY";
});
this.pokemonCandyDarknessOverlay.on("pointerout", () => {
this.scene.ui.hideTooltip();
this.activeTooltip = undefined;
});
}
this.pokemonCandyDarknessOverlay.setCrop(0, 0, 16, candyCropY);
@ -2932,6 +2964,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.abilityCursor = -1;
this.natureCursor = -1;
if (this.activeTooltip === "CANDY") {
const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId);
this.scene.ui.editTooltip("", `${currentFriendship}/${friendshipCap}`);
}
if (species?.forms?.find(f => f.formKey === "female")) {
if (female !== undefined) {
formIndex = female ? 1 : 0;
@ -3081,8 +3118,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
}
if (dexEntry.caughtAttr) {
const ability = this.lastSpecies.getAbility(abilityIndex!); // TODO: is this bang correct?
this.pokemonAbilityText.setText(allAbilities[ability].name);
const ability = allAbilities[this.lastSpecies.getAbility(abilityIndex!)]; // TODO: is this bang correct?
this.pokemonAbilityText.setText(ability.name);
const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1);
this.pokemonAbilityText.setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD));
@ -3091,6 +3128,21 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const passiveAttr = this.scene.gameData.starterData[species.speciesId].passiveAttr;
const passiveAbility = allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]];
if (this.pokemonAbilityText.visible) {
if (this.activeTooltip === "ABILITY") {
this.scene.ui.editTooltip(`${ability.name}`, `${ability.description}`);
}
this.pokemonAbilityText.on("pointerover", () => {
this.scene.ui.showTooltip(`${ability.name}`, `${ability.description}`, true);
this.activeTooltip = "ABILITY";
});
this.pokemonAbilityText.on("pointerout", () => {
this.scene.ui.hideTooltip();
this.activeTooltip = undefined;
});
}
if (passiveAbility) {
const isUnlocked = !!(passiveAttr & PassiveAttr.UNLOCKED);
const isEnabled = !!(passiveAttr & PassiveAttr.ENABLED);
@ -3107,6 +3159,21 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonPassiveText.setAlpha(textAlpha);
this.pokemonPassiveText.setShadowColor(this.getTextColor(textStyle, true));
if (this.activeTooltip === "PASSIVE") {
this.scene.ui.editTooltip(`${passiveAbility.name}`, `${passiveAbility.description}`);
}
if (this.pokemonPassiveText.visible) {
this.pokemonPassiveText.on("pointerover", () => {
this.scene.ui.showTooltip(`${passiveAbility.name}`, `${passiveAbility.description}`, true);
this.activeTooltip = "PASSIVE";
});
this.pokemonPassiveText.on("pointerout", () => {
this.scene.ui.hideTooltip();
this.activeTooltip = undefined;
});
}
const iconPosition = {
x: this.pokemonPassiveText.x + this.pokemonPassiveText.displayWidth + 1,
y: this.pokemonPassiveText.y + this.pokemonPassiveText.displayHeight / 2

View File

@ -244,7 +244,7 @@ export default class UI extends Phaser.GameObjects.Container {
this.tooltipContent = addTextObject(this.scene, 6, 16, "", TextStyle.TOOLTIP_CONTENT);
this.tooltipContent.setName("text-tooltip-content");
this.tooltipContent.setWordWrapWidth(696);
this.tooltipContent.setWordWrapWidth(850);
this.tooltipContainer.add(this.tooltipBg);
this.tooltipContainer.add(this.tooltipTitle);
@ -368,14 +368,13 @@ export default class UI extends Phaser.GameObjects.Container {
return false;
}
getTooltip(): { visible: boolean; title: string; content: string } {
return { visible: this.tooltipContainer.visible, title: this.tooltipTitle.text, content: this.tooltipContent.text };
}
showTooltip(title: string, content: string, overlap?: boolean): void {
this.tooltipContainer.setVisible(true);
this.tooltipTitle.setText(title || "");
const wrappedContent = this.tooltipContent.runWordWrap(content);
this.tooltipContent.setText(wrappedContent);
this.tooltipContent.y = title ? 16 : 4;
this.tooltipBg.width = Math.min(Math.max(this.tooltipTitle.displayWidth, this.tooltipContent.displayWidth) + 12, 684);
this.tooltipBg.height = (title ? 31 : 19) + 10.5 * (wrappedContent.split("\n").length - 1);
this.editTooltip(title, content);
if (overlap) {
(this.scene as BattleScene).uiContainer.moveAbove(this.tooltipContainer, this);
} else {
@ -383,6 +382,15 @@ export default class UI extends Phaser.GameObjects.Container {
}
}
editTooltip(title: string, content: string): void {
this.tooltipTitle.setText(title || "");
const wrappedContent = this.tooltipContent.runWordWrap(content);
this.tooltipContent.setText(wrappedContent);
this.tooltipContent.y = title ? 16 : 4;
this.tooltipBg.width = Math.min(Math.max(this.tooltipTitle.displayWidth, this.tooltipContent.displayWidth) + 12, 838);
this.tooltipBg.height = (title ? 31 : 19) + 10.5 * (wrappedContent.split("\n").length - 1);
}
hideTooltip(): void {
this.tooltipContainer.setVisible(false);
this.tooltipTitle.clearTint();
@ -390,8 +398,12 @@ export default class UI extends Phaser.GameObjects.Container {
update(): void {
if (this.tooltipContainer.visible) {
const reverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12;
this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer!.x / 6 + 2 : this.scene.game.input.mousePointer!.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer!.y / 6 + 2); // TODO: are these bangs correct?
const xReverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12;
const yReverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.y >= this.scene.game.canvas.height - this.tooltipBg.height * 6 - 12;
this.tooltipContainer.setPosition(
!xReverse ? this.scene.game.input.mousePointer!.x / 6 + 2 : this.scene.game.input.mousePointer!.x / 6 - this.tooltipBg.width - 2,
!yReverse ? this.scene.game.input.mousePointer!.y / 6 + 2 : this.scene.game.input.mousePointer!.y / 6 - this.tooltipBg.height - 2,
);
}
}