Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Move] Partially Implement Instruct #4857

Merged
merged 34 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
055eec1
Partially Implement Instruct
Bertie690 Nov 7, 2024
83a4b50
Merge branch 'beta' into beta
Bertie690 Nov 7, 2024
4ae510f
Revert
Bertie690 Nov 7, 2024
f5129af
fix thingy kinda ig
Bertie690 Nov 8, 2024
edf9b12
started testing stuff
Bertie690 Nov 8, 2024
7ac9a3f
test updates
Bertie690 Nov 8, 2024
74fb08a
more test fixes
Bertie690 Nov 10, 2024
22c73bb
prevented softlock with instruct calling itself
Bertie690 Nov 10, 2024
28bce5f
Fixed tests for realises
Bertie690 Nov 11, 2024
d84500b
Merge branch 'beta' into beta
Bertie690 Nov 11, 2024
cee8599
Merge branch 'beta' into beta
Bertie690 Nov 11, 2024
67e5268
changed moveQueue to use unshift
Bertie690 Nov 12, 2024
1cc579c
Merge branch 'beta' of https://github.com/Bertie690/pokerogue into beta
Bertie690 Nov 12, 2024
ceb34f8
Merge branch 'beta' into beta
Bertie690 Nov 12, 2024
1dafc62
Merge branch 'beta' into instruct
Bertie690 Nov 12, 2024
e48e003
minor docs fixes
Bertie690 Nov 12, 2024
b1eea52
minor test desc change
Bertie690 Nov 12, 2024
7c128e8
Fix interaction with multi-hits, update tests
DayKev Nov 13, 2024
e0d37b5
Merge pull request #1 from DayKev/instruct-patch-1
Bertie690 Nov 14, 2024
113de0a
Merge branch 'beta' into instruct
Bertie690 Nov 14, 2024
b499186
Update move.ts
Bertie690 Nov 15, 2024
8b5ce8e
Update move.ts
Bertie690 Nov 15, 2024
eb074a4
Merge branch 'beta' into instruct
Bertie690 Nov 15, 2024
0794a6b
Merge branch 'beta' into instruct
Bertie690 Nov 15, 2024
1d32137
Merge branch 'beta' into instruct
Bertie690 Nov 16, 2024
3657b3e
Fixed protect test
Bertie690 Nov 16, 2024
7e69a79
Fixed Protect test failure
Bertie690 Nov 18, 2024
0221b3e
Merge branch 'beta' into instruct
Bertie690 Nov 18, 2024
99dcbab
Merge branch 'beta' into instruct
Bertie690 Nov 19, 2024
f0c0865
Merge branch 'beta' into instruct
CodeTappert Nov 28, 2024
515e3c5
Merge branch 'beta' into instruct
DayKev Nov 29, 2024
9c7dc52
Merge branch 'beta' into instruct
DayKev Nov 30, 2024
ce60994
Merge branch 'beta' into instruct
Bertie690 Nov 30, 2024
ba2a292
Merge branch 'beta' into instruct
Bertie690 Dec 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/battle-scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2469,6 +2469,24 @@ export default class BattleScene extends SceneBase {
}
}

/**
* Tries to add the input phase to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()}
* @param phase {@linkcode Phase} the phase to be added
* @param targetPhase {@linkcode Phase} the type of phase to search for in {@linkcode phaseQueue}
* @returns `true` if a `targetPhase` was found to append to
*/
appendToPhase(phase: Phase, targetPhase: Constructor<Phase>): boolean {
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase);

if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
this.phaseQueue.splice(targetIndex + 1, 0, phase);
return true;
} else {
this.unshiftPhase(phase);
return false;
}
}

/**
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
* @param message string for MessagePhase
Expand Down
123 changes: 122 additions & 1 deletion src/data/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6728,6 +6728,126 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr {
}
}

/**
* Attribute used for moves that causes the target to repeat their last used move.
*
* Used for [Instruct](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)).
*/
export class RepeatMoveAttr extends MoveEffectAttr {
constructor() {
super(false, { trigger: MoveEffectTrigger.POST_APPLY }); // needed to ensure correct protect interaction
}

/**
* Forces the target to re-use their last used move again
*
* @param user {@linkcode Pokemon} that used the attack
* @param target {@linkcode Pokemon} targeted by the attack
* @param move N/A
* @param args N/A
* @returns `true` if the move succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// get the last move used (excluding status based failures) as well as the corresponding moveset slot
const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE)!;
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move)!;
const moveTargets = lastMove.targets ?? [];

user.scene.queueMessage(i18next.t("moveTriggers:instructingMove", {
userPokemonName: getPokemonNameWithAffix(user),
targetPokemonName: getPokemonNameWithAffix(target)
}));
target.getMoveQueue().unshift({ move: lastMove.move, targets: moveTargets, ignorePP: false });
target.turnData.extraTurns++;
target.scene.appendToPhase(new MovePhase(target.scene, target, moveTargets, movesetMove), MoveEndPhase);
return true;
}

getCondition(): MoveConditionFunc {
return (user, target, move) => {
// TODO: Confirm behavior of instructing move known by target but called by another move
const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE);
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove?.move);
const moveTargets = lastMove?.targets ?? [];
// TODO: Add a way of adding moves to list procedurally rather than a pre-defined blacklist
const unrepeatablemoves = [
// Locking/Continually Executed moves
Moves.OUTRAGE,
Moves.RAGING_FURY,
Moves.ROLLOUT,
Moves.PETAL_DANCE,
Moves.THRASH,
Moves.ICE_BALL,
// Multi-turn Moves
Moves.BIDE,
Moves.SHELL_TRAP,
Moves.BEAK_BLAST,
Moves.FOCUS_PUNCH,
// "First Turn Only" moves
Moves.FAKE_OUT,
Moves.FIRST_IMPRESSION,
Moves.MAT_BLOCK,
// Moves with a recharge turn
Moves.HYPER_BEAM,
Moves.ETERNABEAM,
Moves.FRENZY_PLANT,
Moves.BLAST_BURN,
Moves.HYDRO_CANNON,
Moves.GIGA_IMPACT,
Moves.PRISMATIC_LASER,
Moves.ROAR_OF_TIME,
Moves.ROCK_WRECKER,
Moves.METEOR_ASSAULT,
// Charging & 2-turn moves
Moves.DIG,
Moves.FLY,
Moves.BOUNCE,
Moves.SHADOW_FORCE,
Moves.PHANTOM_FORCE,
Moves.DIVE,
Moves.ELECTRO_SHOT,
Moves.ICE_BURN,
Moves.GEOMANCY,
Moves.FREEZE_SHOCK,
Moves.SKY_DROP,
Moves.SKY_ATTACK,
Moves.SKULL_BASH,
Moves.SOLAR_BEAM,
Moves.SOLAR_BLADE,
Moves.METEOR_BEAM,
// Other moves
Moves.INSTRUCT,
Moves.KINGS_SHIELD,
Moves.SKETCH,
Moves.TRANSFORM,
Moves.MIMIC,
Moves.STRUGGLE,
// TODO: Add Max/G-Move blockage if or when they are implemented
];

if (!movesetMove // called move not in target's moveset (dancer, forgetting the move, etc.)
|| movesetMove.ppUsed === movesetMove.getMovePp() // move out of pp
|| allMoves[lastMove?.move ?? Moves.NONE].isChargingMove() // called move is a charging/recharging move
|| !moveTargets.length // called move has no targets
|| unrepeatablemoves.includes(lastMove?.move ?? Moves.NONE)) { // called move is explicitly in the banlist
return false;
}
return true;
};
}

getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
// TODO: Make the AI acutally use instruct
/* Ideally, the AI would score instruct based on the scorings of the on-field pokemons'
* last used moves at the time of using Instruct (by the time the instructor gets to act)
* with respect to the user's side.
* In 99.9% of cases, this would be the pokemon's ally (unless the target had last
* used a move like Decorate on the user or its ally)
*/
return 2;
}
}

/**
* Attribute used for moves that reduce PP of the target's last used move.
* Used for Spite.
Expand Down Expand Up @@ -9892,7 +10012,8 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.ATK ], -1),
new StatusMove(Moves.INSTRUCT, Type.PSYCHIC, -1, 15, -1, 0, 7)
.ignoresSubstitute()
.unimplemented(),
.attr(RepeatMoveAttr)
.edgeCase(), // incorrect interactions with Gigaton Hammer, Blood Moon & Torment
new AttackMove(Moves.BEAK_BLAST, Type.FLYING, MoveCategory.PHYSICAL, 100, 100, 15, -1, -3, 7)
.attr(BeakBlastHeaderAttr)
.ballBombMove()
Expand Down
6 changes: 6 additions & 0 deletions src/field/pokemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5285,6 +5285,7 @@ export class PokemonBattleSummonData {
export class PokemonTurnData {
public flinched: boolean = false;
public acted: boolean = false;
/** How many times the move should hit the target(s) */
public hitCount: number = 0;
/**
* - `-1` = Calculate how many hits are left
Expand All @@ -5303,6 +5304,11 @@ export class PokemonTurnData {
public switchedInThisTurn: boolean = false;
public failedRunAway: boolean = false;
public joinedRound: boolean = false;
/**
* Used to make sure multi-hits occur properly when the user is
* forced to act again in the same turn
*/
public extraTurns: number = 0;
}

export enum AiType {
Expand Down
10 changes: 9 additions & 1 deletion src/phases/move-effect-phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ export class MoveEffectPhase extends PokemonPhase {

user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);

// If the user is acting again (such as due to Instruct), reset hitsLeft/hitCount so that
// the move executes correctly (ensures all hits of a multi-hit are properly calculated)
if (user.turnData.hitsLeft === 0 && user.turnData.hitCount > 0 && user.turnData.extraTurns > 0) {
user.turnData.hitsLeft = -1;
user.turnData.hitCount = 0;
user.turnData.extraTurns--;
}

/**
* If this phase is for the first hit of the invoked move,
* resolve the move's total hit count. This block combines the
Expand Down Expand Up @@ -313,7 +321,7 @@ export class MoveEffectPhase extends PokemonPhase {
}

/**
* Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs.
* Create a Promise that applies *all* effects from the invoked move's MoveEffectAttrs.
* These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger
* type requires different conditions to be met with respect to the move's hit result.
*/
Expand Down
Loading