mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-10 17:39:31 +02:00
Fixed issues with merging
This commit is contained in:
parent
768fb7036a
commit
5a4f7effb3
@ -10,9 +10,6 @@ export type ArenaTrapTagType =
|
|||||||
| ArenaTagType.STEALTH_ROCK
|
| ArenaTagType.STEALTH_ROCK
|
||||||
| ArenaTagType.IMPRISON;
|
| ArenaTagType.IMPRISON;
|
||||||
|
|
||||||
/** Subset of {@linkcode ArenaTagType}s that are considered delayed attacks */
|
|
||||||
export type ArenaDelayedAttackTagType = ArenaTagType.FUTURE_SIGHT | ArenaTagType.DOOM_DESIRE;
|
|
||||||
|
|
||||||
/** Subset of {@linkcode ArenaTagType}s that create {@link https://bulbapedia.bulbagarden.net/wiki/Category:Screen-creating_moves | screens}. */
|
/** Subset of {@linkcode ArenaTagType}s that create {@link https://bulbapedia.bulbagarden.net/wiki/Category:Screen-creating_moves | screens}. */
|
||||||
export type ArenaScreenTagType = ArenaTagType.REFLECT | ArenaTagType.LIGHT_SCREEN | ArenaTagType.AURORA_VEIL;
|
export type ArenaScreenTagType = ArenaTagType.REFLECT | ArenaTagType.LIGHT_SCREEN | ArenaTagType.AURORA_VEIL;
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import { StatusEffect } from "#enums/status-effect";
|
|||||||
import type { Arena } from "#field/arena";
|
import type { Arena } from "#field/arena";
|
||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import type {
|
import type {
|
||||||
ArenaDelayedAttackTagType,
|
|
||||||
ArenaScreenTagType,
|
ArenaScreenTagType,
|
||||||
ArenaTagTypeData,
|
ArenaTagTypeData,
|
||||||
ArenaTrapTagType,
|
ArenaTrapTagType,
|
||||||
@ -33,7 +32,7 @@ import i18next from "i18next";
|
|||||||
/*
|
/*
|
||||||
ArenaTags are are meant for effects that are tied to the arena (as opposed to a specific pokemon).
|
ArenaTags are are meant for effects that are tied to the arena (as opposed to a specific pokemon).
|
||||||
Examples include (but are not limited to)
|
Examples include (but are not limited to)
|
||||||
- Cross-turn effects that persist even if the user/target switches out, such as Wish, Future Sight, and Happy Hour
|
- Cross-turn effects that persist even if the user/target switches out, such as and Happy Hour
|
||||||
- Effects that are applied to a specific side of the field, such as Crafty Shield, Reflect, and Spikes
|
- Effects that are applied to a specific side of the field, such as Crafty Shield, Reflect, and Spikes
|
||||||
- Field-Effects, like Gravity and Trick Room
|
- Field-Effects, like Gravity and Trick Room
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ Serializable ArenaTags have strict rules for their fields.
|
|||||||
These rules ensure that only the data necessary to reconstruct the tag is serialized, and that the
|
These rules ensure that only the data necessary to reconstruct the tag is serialized, and that the
|
||||||
session loader is able to deserialize saved tags correctly.
|
session loader is able to deserialize saved tags correctly.
|
||||||
|
|
||||||
If the data is static (i.e. it is always the same for all instances of the class, such as the
|
If the data is static (i.e. it is always the same for all instances of the class, such as the
|
||||||
type that is weakened by Mud Sport/Water Sport), then it must not be defined as a field, and must
|
type that is weakened by Mud Sport/Water Sport), then it must not be defined as a field, and must
|
||||||
instead be defined as a getter.
|
instead be defined as a getter.
|
||||||
A static property is also acceptable, though static properties are less ergonomic with inheritance.
|
A static property is also acceptable, though static properties are less ergonomic with inheritance.
|
||||||
@ -1076,48 +1075,6 @@ class StickyWebTag extends ArenaTrapTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Arena Tag class for delayed attacks, such as {@linkcode MoveId.FUTURE_SIGHT} or {@linkcode MoveId.DOOM_DESIRE}.
|
|
||||||
* Delays the attack's effect by a set amount of turns, usually 3 (including the turn the move is used),
|
|
||||||
* and deals damage after the turn count is reached.
|
|
||||||
*/
|
|
||||||
export class DelayedAttackTag extends SerializableArenaTag {
|
|
||||||
public targetIndex: BattlerIndex;
|
|
||||||
public readonly tagType: ArenaDelayedAttackTagType;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
tagType: ArenaTagType.DOOM_DESIRE | ArenaTagType.FUTURE_SIGHT,
|
|
||||||
sourceMove: MoveId | undefined,
|
|
||||||
sourceId: number | undefined,
|
|
||||||
targetIndex: BattlerIndex,
|
|
||||||
side: ArenaTagSide = ArenaTagSide.BOTH,
|
|
||||||
) {
|
|
||||||
super(3, sourceMove, sourceId, side);
|
|
||||||
this.tagType = tagType;
|
|
||||||
this.targetIndex = targetIndex;
|
|
||||||
this.side = side;
|
|
||||||
}
|
|
||||||
|
|
||||||
lapse(arena: Arena): boolean {
|
|
||||||
const ret = super.lapse(arena);
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
// TODO: This should not add to move history (for Spite)
|
|
||||||
globalScene.phaseManager.unshiftNew(
|
|
||||||
"MoveEffectPhase",
|
|
||||||
this.sourceId!,
|
|
||||||
[this.targetIndex],
|
|
||||||
allMoves[this.sourceMove!],
|
|
||||||
MoveUseMode.FOLLOW_UP,
|
|
||||||
); // TODO: are those bangs correct?
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Trick_Room_(move) Trick Room}.
|
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Trick_Room_(move) Trick Room}.
|
||||||
* Reverses the Speed stats for all Pokémon on the field as long as this arena tag is up,
|
* Reverses the Speed stats for all Pokémon on the field as long as this arena tag is up,
|
||||||
@ -1682,7 +1639,7 @@ export function getArenaTag(
|
|||||||
*/
|
*/
|
||||||
export function loadArenaTag(source: ArenaTag | ArenaTagTypeData): ArenaTag {
|
export function loadArenaTag(source: ArenaTag | ArenaTagTypeData): ArenaTag {
|
||||||
const tag =
|
const tag =
|
||||||
getArenaTag(source.tagType, source.sourceId, source.sourceMove, source.turnCount, source.side) ?? new NoneTag();
|
getArenaTag(source.tagType, source.turnCount, source.sourceMove, source.sourceId, source.side) ?? new NoneTag();
|
||||||
tag.loadTag(source);
|
tag.loadTag(source);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
@ -3141,7 +3141,7 @@ abstract class AddPositionalTagAttr extends OverrideMoveEffectAttr {
|
|||||||
|
|
||||||
public override getCondition(): MoveConditionFunc {
|
public override getCondition(): MoveConditionFunc {
|
||||||
// Check the arena if another similar positional tag is active and affecting the same slot
|
// Check the arena if another similar positional tag is active and affecting the same slot
|
||||||
return (_user, target, move) => globalScene.arena.positionalTagManager.canAddTag(this.tagType, target.getBattlerIndex(), move.id)
|
return (_user, target, move) => globalScene.arena.positionalTagManager.canAddTag(this.tagType, target.getBattlerIndex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3201,7 +3201,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
|||||||
|
|
||||||
public override getCondition(): MoveConditionFunc {
|
public override getCondition(): MoveConditionFunc {
|
||||||
// Check the arena if another similar attack is active and affecting the same slot
|
// Check the arena if another similar attack is active and affecting the same slot
|
||||||
return (_user, target, move) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.DELAYED_ATTACK, target.getBattlerIndex(), move.id)
|
return (_user, target, move) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.DELAYED_ATTACK, target.getBattlerIndex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3219,8 +3219,8 @@ export class WishAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override getCondition(): MoveConditionFunc {
|
public override getCondition(): MoveConditionFunc {
|
||||||
// Check the arena if another similar attack is active and affecting the same slot
|
// Check the arena if another wish is active and affecting the same slot
|
||||||
return (_user, target, move) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.WISH, target.getBattlerIndex(), move.id)
|
return (_user, target) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.WISH, target.getBattlerIndex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,16 +7,18 @@ import type { Constructor } from "#utils/common";
|
|||||||
* Add a new {@linkcode PositionalTag} to the arena.
|
* Add a new {@linkcode PositionalTag} to the arena.
|
||||||
* @param tagType - The {@linkcode PositionalTagType} to create
|
* @param tagType - The {@linkcode PositionalTagType} to create
|
||||||
* @param args - The arguments needed to instantize the given tag
|
* @param args - The arguments needed to instantize the given tag
|
||||||
|
* @returns The newly created tag.
|
||||||
* @remarks
|
* @remarks
|
||||||
* This function does not perform any checking if the added tag is valid.
|
* This function does not perform any checking if the added tag is valid.
|
||||||
*/
|
*/
|
||||||
export function loadPositionalTag<T extends PositionalTagType>({
|
export function loadPositionalTag<T extends PositionalTagType>({
|
||||||
tagType,
|
tagType,
|
||||||
...args
|
...args
|
||||||
}: serializedPosTagParamMap[T]): posTagInstanceMap[T];
|
}: serializedPosTagMap[T]): posTagInstanceMap[T];
|
||||||
/**
|
/**
|
||||||
* Add a new {@linkcode PositionalTag} to the arena.
|
* Add a new {@linkcode PositionalTag} to the arena.
|
||||||
* @param tag - The {@linkcode SerializedPositionalTag} to instantiate
|
* @param tag - The {@linkcode SerializedPositionalTag} to instantiate
|
||||||
|
* @returns The newly created tag.
|
||||||
* @remarks
|
* @remarks
|
||||||
* This function does not perform any checking if the added tag is valid.
|
* This function does not perform any checking if the added tag is valid.
|
||||||
*/
|
*/
|
||||||
@ -24,12 +26,13 @@ export function loadPositionalTag(tag: SerializedPositionalTag): PositionalTag;
|
|||||||
export function loadPositionalTag<T extends PositionalTagType>({
|
export function loadPositionalTag<T extends PositionalTagType>({
|
||||||
tagType,
|
tagType,
|
||||||
...rest
|
...rest
|
||||||
}: serializedPosTagParamMap[T]): posTagInstanceMap[T] {
|
}: serializedPosTagMap[T]): posTagInstanceMap[T] {
|
||||||
// Note: We need 2 type assertions here:
|
// Note: We need 2 type assertions here:
|
||||||
// 1 because TS doesn't narrow the type of TagClass correctly based on `T`.
|
// 1 because TS doesn't narrow the type of TagClass correctly based on `T`.
|
||||||
// It converts it into `new (DelayedAttackTag | WishTag) => DelayedAttackTag & WishTag`
|
// It converts it into `new (DelayedAttackTag | WishTag) => DelayedAttackTag & WishTag`
|
||||||
const tagClass = posTagConstructorMap[tagType] as new (args: posTagParamMap[T]) => posTagInstanceMap[T];
|
const tagClass = posTagConstructorMap[tagType] as new (args: posTagParamMap[T]) => posTagInstanceMap[T];
|
||||||
// 2 because TS doesn't narrow `Omit<{tagType: T} & posTagParamMap[T], "tagType"> into `posTagParamMap[T]`
|
// 2 because TS doesn't narrow the type of `rest` correctly
|
||||||
|
// (from `Omit<serializedPosTagParamMap[T], "tagType"> into `posTagParamMap[T]`)
|
||||||
return new tagClass(rest as unknown as posTagParamMap[T]);
|
return new tagClass(rest as unknown as posTagParamMap[T]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,10 +41,11 @@ const posTagConstructorMap = Object.freeze({
|
|||||||
[PositionalTagType.DELAYED_ATTACK]: DelayedAttackTag,
|
[PositionalTagType.DELAYED_ATTACK]: DelayedAttackTag,
|
||||||
[PositionalTagType.WISH]: WishTag,
|
[PositionalTagType.WISH]: WishTag,
|
||||||
}) satisfies {
|
}) satisfies {
|
||||||
|
// NB: This `satisfies` block ensures that all tag types have corresponding entries in the map.
|
||||||
[k in PositionalTagType]: Constructor<PositionalTag & { tagType: k }>;
|
[k in PositionalTagType]: Constructor<PositionalTag & { tagType: k }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Type mapping tag types to their constructors. */
|
/** Type mapping positional tag types to their constructors. */
|
||||||
type posTagMap = typeof posTagConstructorMap;
|
type posTagMap = typeof posTagConstructorMap;
|
||||||
|
|
||||||
/** Type mapping all positional tag types to their instances. */
|
/** Type mapping all positional tag types to their instances. */
|
||||||
@ -54,10 +58,13 @@ type posTagParamMap = {
|
|||||||
[k in PositionalTagType]: ConstructorParameters<posTagMap[k]>[0];
|
[k in PositionalTagType]: ConstructorParameters<posTagMap[k]>[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Type mapping all positional tag types to their constructors' parameters, alongside the `tagType` selector. */
|
/**
|
||||||
type serializedPosTagParamMap = {
|
* Type mapping all positional tag types to their constructors' parameters, alongside the `tagType` selector.
|
||||||
|
* Equivalent to their serialized representations.
|
||||||
|
*/
|
||||||
|
export type serializedPosTagMap = {
|
||||||
[k in PositionalTagType]: posTagParamMap[k] & { tagType: k };
|
[k in PositionalTagType]: posTagParamMap[k] & { tagType: k };
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Union type containing all serialized {@linkcode PositionalTag}s. */
|
/** Union type containing all serialized {@linkcode PositionalTag}s. */
|
||||||
export type SerializedPositionalTag = EnumValues<serializedPosTagParamMap>;
|
export type SerializedPositionalTag = EnumValues<serializedPosTagMap>;
|
||||||
|
@ -13,6 +13,7 @@ export interface SerializedArenaData {
|
|||||||
weather: NonFunctionProperties<Weather> | null;
|
weather: NonFunctionProperties<Weather> | null;
|
||||||
terrain: NonFunctionProperties<Terrain> | null;
|
terrain: NonFunctionProperties<Terrain> | null;
|
||||||
tags?: ArenaTagTypeData[];
|
tags?: ArenaTagTypeData[];
|
||||||
|
positionalTags: SerializedPositionalTag[];
|
||||||
playerTerasUsed?: number;
|
playerTerasUsed?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,17 +35,20 @@ export class ArenaData {
|
|||||||
?.filter((tag): tag is SerializableArenaTag => tag instanceof SerializableArenaTag) ?? [];
|
?.filter((tag): tag is SerializableArenaTag => tag instanceof SerializableArenaTag) ?? [];
|
||||||
|
|
||||||
this.playerTerasUsed = source.playerTerasUsed ?? 0;
|
this.playerTerasUsed = source.playerTerasUsed ?? 0;
|
||||||
this.positionalTags = (sourceArena ? sourceArena.positionalTagManager.tags : source.positionalTags) ?? [];
|
|
||||||
|
|
||||||
if (source instanceof Arena) {
|
if (source instanceof Arena) {
|
||||||
this.biome = source.biomeType;
|
this.biome = source.biomeType;
|
||||||
this.weather = source.weather;
|
this.weather = source.weather;
|
||||||
this.terrain = source.terrain;
|
this.terrain = source.terrain;
|
||||||
|
// The assertion here is ok - we ensure that all tags are inside the `posTagConstructorMap` map,
|
||||||
|
// and that all `PositionalTags` will become their respective interfaces when serialized and de-serialized.
|
||||||
|
this.positionalTags = (source.positionalTagManager.tags as unknown as SerializedPositionalTag[]) ?? [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.biome = source.biome;
|
this.biome = source.biome;
|
||||||
this.weather = source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null;
|
this.weather = source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null;
|
||||||
this.terrain = source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null;
|
this.terrain = source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null;
|
||||||
|
this.positionalTags = source.positionalTags ?? [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
test/types/positional-tags.test-d.ts
Normal file
29
test/types/positional-tags.test-d.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import type { SerializedPositionalTag, serializedPosTagMap } from "#data/positional-tags/load-positional-tag";
|
||||||
|
import type { DelayedAttackTag, WishTag } from "#data/positional-tags/positional-tag";
|
||||||
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
|
import type { Mutable, NonFunctionPropertiesRecursive } from "#types/type-helpers";
|
||||||
|
import { describe, expectTypeOf, it } from "vitest";
|
||||||
|
|
||||||
|
// Needed to get around properties being readonly in certain classes
|
||||||
|
type NonFunctionMutable<T> = Mutable<NonFunctionPropertiesRecursive<T>>;
|
||||||
|
|
||||||
|
describe("serializedPositionalTagMap", () => {
|
||||||
|
it("should contain representations of each tag's serialized form", () => {
|
||||||
|
expectTypeOf<serializedPosTagMap[PositionalTagType.DELAYED_ATTACK]>().branded.toEqualTypeOf<
|
||||||
|
NonFunctionMutable<DelayedAttackTag>
|
||||||
|
>();
|
||||||
|
expectTypeOf<serializedPosTagMap[PositionalTagType.WISH]>().branded.toEqualTypeOf<NonFunctionMutable<WishTag>>();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("SerializedPositionalTag", () => {
|
||||||
|
it("should accept a union of all serialized tag forms", () => {
|
||||||
|
expectTypeOf<SerializedPositionalTag>().branded.toEqualTypeOf<
|
||||||
|
NonFunctionMutable<DelayedAttackTag> | NonFunctionMutable<WishTag>
|
||||||
|
>();
|
||||||
|
});
|
||||||
|
it("should accept a union of all unserialized tag forms", () => {
|
||||||
|
expectTypeOf<WishTag>().toExtend<SerializedPositionalTag>();
|
||||||
|
expectTypeOf<DelayedAttackTag>().toExtend<SerializedPositionalTag>();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user