xDasone 2023-10-28 14:37:17 -07:00
commit 95f645dd23
38 changed files with 2304 additions and 1694 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

View File

@ -49024,7 +49024,7 @@
}
},
{
"filename": "555standard_01.png",
"filename": "555_01.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49045,7 +49045,7 @@
}
},
{
"filename": "555standard_02.png",
"filename": "555_02.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49066,7 +49066,7 @@
}
},
{
"filename": "555standard_03.png",
"filename": "555_03.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49087,7 +49087,7 @@
}
},
{
"filename": "555standard_04.png",
"filename": "555_04.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49108,7 +49108,7 @@
}
},
{
"filename": "555standard_05.png",
"filename": "555_05.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49129,7 +49129,7 @@
}
},
{
"filename": "555standard_06.png",
"filename": "555_06.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49150,7 +49150,7 @@
}
},
{
"filename": "555standard_07.png",
"filename": "555_07.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49171,7 +49171,7 @@
}
},
{
"filename": "555standard_08.png",
"filename": "555_08.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49192,7 +49192,7 @@
}
},
{
"filename": "555standard_09.png",
"filename": "555_09.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49213,7 +49213,7 @@
}
},
{
"filename": "555standard_10.png",
"filename": "555_10.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49234,7 +49234,7 @@
}
},
{
"filename": "555standard_11.png",
"filename": "555_11.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49255,7 +49255,7 @@
}
},
{
"filename": "555standard_12.png",
"filename": "555_12.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49276,7 +49276,7 @@
}
},
{
"filename": "555standard_13.png",
"filename": "555_13.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49297,7 +49297,7 @@
}
},
{
"filename": "555standard_14.png",
"filename": "555_14.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49318,7 +49318,7 @@
}
},
{
"filename": "555standard_15.png",
"filename": "555_15.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49339,7 +49339,7 @@
}
},
{
"filename": "555standard_16.png",
"filename": "555_16.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -49360,7 +49360,7 @@
}
},
{
"filename": "555standard_17.png",
"filename": "555_17.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50809,7 +50809,7 @@
}
},
{
"filename": "555standard_18.png",
"filename": "555_18.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50830,7 +50830,7 @@
}
},
{
"filename": "555standard_19.png",
"filename": "555_19.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50851,7 +50851,7 @@
}
},
{
"filename": "555standard_20.png",
"filename": "555_20.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50872,7 +50872,7 @@
}
},
{
"filename": "555standard_21.png",
"filename": "555_21.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50893,7 +50893,7 @@
}
},
{
"filename": "555standard_22.png",
"filename": "555_22.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50914,7 +50914,7 @@
}
},
{
"filename": "555standard_23.png",
"filename": "555_23.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50935,7 +50935,7 @@
}
},
{
"filename": "555standard_24.png",
"filename": "555_24.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50956,7 +50956,7 @@
}
},
{
"filename": "555standard_25.png",
"filename": "555_25.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50977,7 +50977,7 @@
}
},
{
"filename": "555standard_26.png",
"filename": "555_26.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -50998,7 +50998,7 @@
}
},
{
"filename": "555standard_27.png",
"filename": "555_27.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51019,7 +51019,7 @@
}
},
{
"filename": "555standard_28.png",
"filename": "555_28.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51040,7 +51040,7 @@
}
},
{
"filename": "555standard_29.png",
"filename": "555_29.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51061,7 +51061,7 @@
}
},
{
"filename": "555standard_30.png",
"filename": "555_30.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51082,7 +51082,7 @@
}
},
{
"filename": "555standard_31.png",
"filename": "555_31.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51103,7 +51103,7 @@
}
},
{
"filename": "555standard_32.png",
"filename": "555_32.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51124,7 +51124,7 @@
}
},
{
"filename": "555standard_33.png",
"filename": "555_33.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
@ -51145,7 +51145,7 @@
}
},
{
"filename": "555standard_34.png",
"filename": "555_34.png",
"rotated": false,
"trimmed": true,
"sourceSize": {

View File

@ -119,7 +119,18 @@ export class Arena {
getFormIndex(species: PokemonSpecies) {
if (!species.canChangeForm && species.forms?.length)
return Utils.randSeedInt(species.forms.length); // TODO: Base on biome
return Utils.randSeedInt(species.forms.length);
switch (species.speciesId) {
case Species.BURMY:
case Species.WORMADAM:
switch (this.biomeType) {
case Biome.BEACH:
return 1;
case Biome.CITY:
return 2;
}
break;
}
return 0;
}

View File

@ -16,7 +16,7 @@ import { EvolutionPhase } from "./evolution-phase";
import { BattlePhase } from "./battle-phase";
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat";
import { Biome, biomeLinks } from "./data/biome";
import { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
import { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag";
import { getPokemonMessage } from "./messages";
@ -246,7 +246,7 @@ export class EncounterPhase extends BattlePhase {
battle.enemyParty[e] = battle.trainer.genPartyMember(e);
else {
const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, null, true);
battle.enemyParty[e] = new EnemyPokemon(this.scene, enemySpecies, level);
battle.enemyParty[e] = new EnemyPokemon(this.scene, enemySpecies, level, false);
}
}
const enemyPokemon = this.scene.getEnemyParty()[e];
@ -256,7 +256,7 @@ export class EncounterPhase extends BattlePhase {
this.scene.gameData.setPokemonSeen(enemyPokemon);
}
if (this.scene.gameMode === GameMode.CLASSIC && battle.waveIndex === 200 && enemyPokemon.species.speciesId === Species.ETERNATUS)
if (this.scene.gameMode === GameMode.CLASSIC && (battle.waveIndex === 200 || !(battle.waveIndex % 250)) && enemyPokemon.species.speciesId === Species.ETERNATUS)
enemyPokemon.formIndex = 1;
loadEnemyAssets.push(enemyPokemon.loadAssets());
@ -635,7 +635,7 @@ export class SummonPhase extends PartyMemberPokemonPhase {
this.scene.field.add(pokemon);
if (!this.player) {
const playerPokemon = this.scene.getPlayerPokemon() as Pokemon;
if (playerPokemon.visible)
if (playerPokemon?.visible)
this.scene.field.moveBelow(pokemon, playerPokemon);
this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id);
this.scene.updateModifiers(false);
@ -937,6 +937,15 @@ export class CommandPhase extends FieldPhase {
start() {
super.start();
if (this.fieldIndex) {
const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1];
if (allyCommand.command === Command.BALL || allyCommand.command === Command.RUN)
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand.command, skip: true };
}
if (this.scene.currentBattle.turnCommands[this.fieldIndex]?.skip)
return this.end();
const playerPokemon = this.scene.getPlayerField()[this.fieldIndex];
const moveQueue = playerPokemon.getMoveQueue();
@ -968,10 +977,12 @@ export class CommandPhase extends FieldPhase {
switch (command) {
case Command.FIGHT:
if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean)) {
let useStruggle = false;
if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean) || (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) {
const moveId = !useStruggle ? playerPokemon.moveset[cursor].moveId : Moves.STRUGGLE;
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor,
move: cursor > -1 ? { move: playerPokemon.moveset[cursor].moveId, targets: [] } : null, args: args }; // TODO: Struggle logic
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, cursor > -1 ? playerPokemon.moveset[cursor].moveId : Moves.NONE) : args[2];
move: cursor > -1 ? { move: moveId, targets: [] } : null, args: args };
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, cursor > -1 ? moveId : Moves.NONE) : args[2];
console.log(moveTargets, playerPokemon.name);
if (moveTargets.targets.length <= 1 || moveTargets.multiple)
turnCommand.move.targets = moveTargets.targets;
@ -989,13 +1000,12 @@ export class CommandPhase extends FieldPhase {
}, null, true);
}
}
break;
case Command.BALL:
if (this.scene.arena.biomeType === Biome.END) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(`A strange force\nprevents using Poké Balls.`, null, () => {
this.scene.ui.showText(`An unseen force\nprevents using Poké Balls.`, null, () => {
this.scene.ui.showText(null, 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true);
@ -1006,21 +1016,39 @@ export class CommandPhase extends FieldPhase {
this.scene.ui.showText(null, 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true);
} else if (cursor < 4) {
} else {
const targets = this.scene.getEnemyField().filter(p => p.isActive(true)).map(p => p.getBattlerIndex());
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
this.scene.currentBattle.turnPokeballCounts[cursor as PokeballType]--;
if (targets.length > 1)
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
else
this.scene.currentBattle.turnCommands[this.fieldIndex].targets = targets;
success = true;
if (targets.length > 1) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(`You can only throw a Poké Ball\nwhen there is one Pokémon remaining!`, null, () => {
this.scene.ui.showText(null, 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true);
} else if (cursor < 4) {
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
if (targets.length > 1)
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
else {
this.scene.currentBattle.turnCommands[this.fieldIndex].targets = targets;
if (this.fieldIndex)
this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true;
}
success = true;
}
}
break;
case Command.POKEMON:
case Command.RUN:
const isSwitch = command === Command.POKEMON;
if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) {
if (!isSwitch && this.scene.arena.biomeType === Biome.END) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(`An unseen force\nprevents escape.`, null, () => {
this.scene.ui.showText(null, 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true);
} else if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(`You can't run\nfrom a trainer battle!`, null, () => {
@ -1038,6 +1066,8 @@ export class CommandPhase extends FieldPhase {
? { command: Command.POKEMON, cursor: cursor, args: args }
: { command: Command.RUN };
success = true;
if (this.fieldIndex)
this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true;
} else if (trapTag) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE);
@ -1058,9 +1088,6 @@ export class CommandPhase extends FieldPhase {
cancel() {
if (this.fieldIndex) {
const lastCommand = this.scene.currentBattle.turnCommands[0];
if (lastCommand.command === Command.BALL)
this.scene.currentBattle.turnPokeballCounts[lastCommand.cursor]++;
this.scene.unshiftPhase(new CommandPhase(this.scene, 0));
this.scene.unshiftPhase(new CommandPhase(this.scene, 1));
this.end();
@ -1116,12 +1143,12 @@ export class SelectTargetPhase extends PokemonPhase {
this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (cursor: integer) => {
this.scene.ui.setMode(Mode.MESSAGE);
if (cursor === -1) {
if (turnCommand.command === Command.BALL)
this.scene.currentBattle.turnPokeballCounts[turnCommand.cursor]++;
this.scene.currentBattle.turnCommands[this.fieldIndex] = null;
this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex));
} else
turnCommand.targets = [ cursor ];
if (turnCommand.command === Command.BALL && this.fieldIndex)
this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true;
this.end();
});
}
@ -1168,6 +1195,9 @@ export class TurnStartPhase extends FieldPhase {
const pokemon = field[o];
const turnCommand = this.scene.currentBattle.turnCommands[o];
if (turnCommand.skip)
continue;
switch (turnCommand.command) {
case Command.FIGHT:
const queuedMove = turnCommand.move;
@ -1263,7 +1293,7 @@ export class BattleEndPhase extends BattlePhase {
pokemon.resetBattleSummonData();
}
this.scene.clearEnemyModifiers();
this.scene.clearEnemyHeldItemModifiers();
const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier) as LapsingPersistentModifier[];
for (let m of lapsingModifiers) {
@ -2141,9 +2171,7 @@ export class ModifierRewardPhase extends BattlePhase {
constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) {
super(scene);
this.modifierType = modifierTypeFunc();
if (!this.modifierType.id)
this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc);
this.modifierType = getModifierType(modifierTypeFunc);
}
start() {
@ -2664,7 +2692,7 @@ export class AttemptCapturePhase extends PokemonPhase {
this.scene.getPlayerField().filter(p => p.isActive()).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id));
pokemon.hp = 0;
pokemon.trySetStatus(StatusEffect.FAINT);
this.scene.clearEnemyModifiers();
this.scene.clearEnemyHeldItemModifiers();
this.scene.field.remove(pokemon, true);
};
const addToParty = () => {

View File

@ -16,7 +16,7 @@ import { GameData } from './system/game-data';
import StarterSelectUiHandler from './ui/starter-select-ui-handler';
import { TextStyle, addTextObject } from './ui/text';
import { Moves, initMoves } from './data/move';
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type';
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getModifierType, modifierTypes } from './modifier/modifier-type';
import AbilityBar from './ui/ability-bar';
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, applyAbAttrs, initAbilities } from './data/ability';
import Battle, { BattleType, FixedBattleConfig, fixedBattles } from './battle';
@ -104,7 +104,7 @@ export default class BattleScene extends Phaser.Scene {
private modifierBar: ModifierBar;
private enemyModifierBar: ModifierBar;
private modifiers: PersistentModifier[];
private enemyModifiers: PokemonHeldItemModifier[];
private enemyModifiers: PersistentModifier[];
public uiContainer: Phaser.GameObjects.Container;
public ui: UI;
@ -1148,7 +1148,7 @@ export default class BattleScene extends Phaser.Scene {
});
}
addEnemyModifier(itemModifier: PokemonHeldItemModifier): Promise<void> {
addEnemyModifier(itemModifier: PersistentModifier): Promise<void> {
return new Promise(resolve => {
itemModifier.add(this.enemyModifiers, false);
this.updateModifiers(false).then(() => resolve());
@ -1206,7 +1206,7 @@ export default class BattleScene extends Phaser.Scene {
removePartyMemberModifiers(partyMemberIndex: integer): Promise<void> {
return new Promise(resolve => {
const pokemonId = this.getParty()[partyMemberIndex].id;
const modifiersToRemove = this.modifiers.filter(m => (m instanceof PokemonHeldItemModifier) && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
const modifiersToRemove = this.modifiers.filter(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
for (let m of modifiersToRemove)
this.modifiers.splice(this.modifiers.indexOf(m), 1);
this.updateModifiers().then(() => resolve());
@ -1233,12 +1233,10 @@ export default class BattleScene extends Phaser.Scene {
for (let c = 0; c < chances; c++) {
if (!Utils.randSeedInt(modifierChance))
count++;
if (count === 12)
break;
}
if (isBoss)
count = Math.max(count, Math.floor(chances / 2));
getEnemyModifierTypesForWave(waveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD)
getEnemyModifierTypesForWave(waveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, this.gameMode)
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false));
});
@ -1246,8 +1244,17 @@ export default class BattleScene extends Phaser.Scene {
});
}
clearEnemyModifiers(): void {
this.enemyModifiers.splice(0, this.enemyModifiers.length);
generateEnemyBuffModifier(): void{
const enemyBuffModifierTypes = [ modifierTypes.ENEMY_DAMAGE_BOOSTER, modifierTypes.ENEMY_DAMAGE_REDUCTION ];
this.executeWithSeedOffset(() => {
(getModifierType(Phaser.Math.RND.pick(enemyBuffModifierTypes)).newModifier() as PersistentModifier).add(this.enemyModifiers, false);
}, Math.floor(this.currentBattle.waveIndex / 50));
}
clearEnemyHeldItemModifiers(): void {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier);
for (let m of modifiersToRemove)
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
this.updateModifiers(false).then(() => this.updateUIPositions());
}

View File

@ -24,6 +24,7 @@ export interface TurnCommand {
cursor?: integer;
move?: QueuedMove;
targets?: BattlerIndex[];
skip?: boolean;
args?: any[];
};
@ -42,7 +43,6 @@ export default class Battle {
public started: boolean;
public turn: integer;
public turnCommands: TurnCommands;
public turnPokeballCounts: PokeballCounts;
public playerParticipantIds: Set<integer> = new Set<integer>();
public escapeAttempts: integer = 0;
public lastMove: Moves;
@ -94,7 +94,6 @@ export default class Battle {
incrementTurn(scene: BattleScene): void {
this.turn++;
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
this.turnPokeballCounts = Object.assign({}, scene.pokeballCounts);
}
addParticipant(playerPokemon: PlayerPokemon): void {

View File

@ -2,7 +2,7 @@ import Pokemon, { HitResult, PokemonMove } from "../pokemon";
import { Type } from "./type";
import * as Utils from "../utils";
import { BattleStat, getBattleStatName } from "./battle-stat";
import { DamagePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
import { DamagePhase, ObtainStatusEffectPhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
import { getPokemonMessage } from "../messages";
import { Weather, WeatherType } from "./weather";
import { BattlerTag, BattlerTagType } from "./battler-tag";
@ -290,9 +290,9 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
}
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance) {
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)];
return attacker.trySetStatus(effect);
pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, attacker.getBattlerIndex(), effect));
}
return false;

View File

@ -13,6 +13,7 @@ import { BlockRecoilDamageAttr, applyAbAttrs } from "./ability";
import { PokemonHeldItemModifier } from "../modifier/modifier";
import { BattlerIndex } from "../battle";
import { Stat } from "./pokemon-stat";
import { Species } from "./species";
export enum MoveCategory {
PHYSICAL,
@ -977,7 +978,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
}
getDamage(user: Pokemon, target: Pokemon, move: Move): number {
return user.level * (Utils.randIntRange(50, 150) * 0.01);
return Math.max(Math.floor(user.level * (Utils.randIntRange(50, 150) * 0.01)), 1);
}
}
@ -1155,18 +1156,22 @@ export class MultiHitAttr extends MoveAttr {
export class StatusEffectAttr extends MoveEffectAttr {
public effect: StatusEffect;
public cureTurn: integer;
public overrideStatus: boolean;
constructor(effect: StatusEffect, selfTarget?: boolean, cureTurn?: integer) {
constructor(effect: StatusEffect, selfTarget?: boolean, cureTurn?: integer, overrideStatus?: boolean) {
super(selfTarget, MoveEffectTrigger.HIT);
this.effect = effect;
this.cureTurn = cureTurn;
this.overrideStatus = !!overrideStatus;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const statusCheck = move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance;
if (statusCheck) {
const pokemon = this.selfTarget ? user : target;
if (pokemon.status)
pokemon.resetStatus();
if (!pokemon.status || (pokemon.status.effect === this.effect && move.chance < 0)) {
user.scene.unshiftPhase(new ObtainStatusEffectPhase(user.scene, pokemon.getBattlerIndex(), this.effect, this.cureTurn));
return true;
@ -1296,6 +1301,8 @@ export class OneHitKOAttr extends MoveEffectAttr {
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (target.species.speciesId === Species.ETERNATUS && target.formIndex === 1)
return false;
target.damage(target.hp, true);
user.scene.queueMessage('It\'s a one-hit KO!');
return true;
@ -2299,7 +2306,7 @@ export function initMoves() {
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
new AttackMove(Moves.SCRATCH, "Scratch", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, "", -1, 0, 1),
new AttackMove(Moves.VISE_GRIP, "Vise Grip", Type.NORMAL, MoveCategory.PHYSICAL, 55, 100, 30, -1, "", -1, 0, 1),
new AttackMove(Moves.GUILLOTINE, "Guillotine", Type.NORMAL, MoveCategory.PHYSICAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1)
new AttackMove(Moves.GUILLOTINE, "Guillotine", Type.NORMAL, MoveCategory.PHYSICAL, 250, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1)
.attr(OneHitKOAttr),
new AttackMove(Moves.RAZOR_WIND, "Razor Wind", Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, "Charges on first turn, attacks on second. High critical hit ratio.", -1, 0, 1)
.attr(ChargeAttr, ChargeAnim.RAZOR_WIND_CHARGING, 'whipped\nup a whirlwind!')
@ -2340,7 +2347,7 @@ export function initMoves() {
new AttackMove(Moves.HORN_ATTACK, "Horn Attack", Type.NORMAL, MoveCategory.PHYSICAL, 65, 100, 25, -1, "", -1, 0, 1),
new AttackMove(Moves.FURY_ATTACK, "Fury Attack", Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, "Hits 2-5 times in one turn.", -1, 0, 1)
.attr(MultiHitAttr),
new AttackMove(Moves.HORN_DRILL, "Horn Drill", Type.NORMAL, MoveCategory.PHYSICAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1)
new AttackMove(Moves.HORN_DRILL, "Horn Drill", Type.NORMAL, MoveCategory.PHYSICAL, 250, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1)
.attr(OneHitKOAttr),
new AttackMove(Moves.TACKLE, "Tackle", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, "", -1, 0, 1),
new AttackMove(Moves.BODY_SLAM, "Body Slam", Type.NORMAL, MoveCategory.PHYSICAL, 85, 100, 15, 66, "May paralyze opponent.", 30, 0, 1)
@ -2478,7 +2485,7 @@ export function initMoves() {
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true)
.makesContact(false)
.target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.FISSURE, "Fissure", Type.GROUND, MoveCategory.PHYSICAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1)
new AttackMove(Moves.FISSURE, "Fissure", Type.GROUND, MoveCategory.PHYSICAL, 250, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 1)
.attr(OneHitKOAttr)
.makesContact(false),
new AttackMove(Moves.DIG, "Dig", Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, 55, "Digs underground on first turn, attacks on second. Can also escape from caves.", -1, 0, 1)
@ -2632,7 +2639,7 @@ export function initMoves() {
.attr(MultiHitAttr, MultiHitType._2)
.makesContact(false),
new SelfStatusMove(Moves.REST, "Rest", Type.PSYCHIC, -1, 5, 85, "User sleeps for 2 turns, but user is fully healed.", -1, 0, 1)
.attr(StatusEffectAttr, StatusEffect.SLEEP, true, 3)
.attr(StatusEffectAttr, StatusEffect.SLEEP, true, 3, true)
.attr(HealAttr, 1, true)
.condition((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect !== StatusEffect.SLEEP),
new AttackMove(Moves.ROCK_SLIDE, "Rock Slide", Type.ROCK, MoveCategory.PHYSICAL, 75, 90, 10, 86, "May cause flinching.", 30, 0, 1)
@ -2744,9 +2751,9 @@ export function initMoves() {
new StatusMove(Moves.SANDSTORM, "Sandstorm", Type.ROCK, -1, 10, 51, "Creates a sandstorm for 5 turns.", -1, 0, 2)
.attr(WeatherChangeAttr, WeatherType.SANDSTORM)
.target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.GIGA_DRAIN, "Giga Drain", Type.GRASS, MoveCategory.SPECIAL, 75, 100, 10, 111, "User recovers half the HP inflicted on opponent.", -1, 4, 2)
new AttackMove(Moves.GIGA_DRAIN, "Giga Drain", Type.GRASS, MoveCategory.SPECIAL, 75, 100, 10, 111, "User recovers half the HP inflicted on opponent.", -1, 0, 2)
.attr(HitHealAttr),
new SelfStatusMove(Moves.ENDURE, "Endure (N)", Type.NORMAL, -1, 10, 47, "Always left with at least 1 HP, but may fail if used consecutively.", -1, 0, 2),
new SelfStatusMove(Moves.ENDURE, "Endure (N)", Type.NORMAL, -1, 10, 47, "Always left with at least 1 HP, but may fail if used consecutively.", -1, 4, 2),
new StatusMove(Moves.CHARM, "Charm", Type.FAIRY, 100, 20, 2, "Sharply lowers opponent's Attack.", -1, 0, 2)
.attr(StatChangeAttr, BattleStat.ATK, -2),
new AttackMove(Moves.ROLLOUT, "Rollout", Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, "Doubles in power each turn for 5 turns.", -1, 0, 2)
@ -2919,7 +2926,7 @@ export function initMoves() {
new AttackMove(Moves.SECRET_POWER, "Secret Power (N)", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, "Effects of the attack vary with the location.", 30, 0, 3)
.makesContact(false),
new AttackMove(Moves.DIVE, "Dive", Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, "Dives underwater on first turn, attacks on second turn.", -1, 0, 3)
.attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, 'hid\nunderwater!')
.attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, 'hid\nunderwater!', BattlerTagType.UNDERGROUND)
.ignoresVirtual(),
new AttackMove(Moves.ARM_THRUST, "Arm Thrust", Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, "Hits 2-5 times in one turn.", -1, 0, 3)
.attr(MultiHitAttr),
@ -3000,7 +3007,7 @@ export function initMoves() {
new AttackMove(Moves.SAND_TOMB, "Sand Tomb", Type.GROUND, MoveCategory.PHYSICAL, 35, 85, 15, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 3)
.attr(TrapAttr, BattlerTagType.SAND_TOMB)
.makesContact(false),
new AttackMove(Moves.SHEER_COLD, "Sheer Cold", Type.ICE, MoveCategory.SPECIAL, -1, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 3)
new AttackMove(Moves.SHEER_COLD, "Sheer Cold", Type.ICE, MoveCategory.SPECIAL, 250, 30, 5, -1, "One-Hit-KO, if it hits.", -1, 0, 3)
.attr(OneHitKOAttr),
new AttackMove(Moves.MUDDY_WATER, "Muddy Water", Type.WATER, MoveCategory.SPECIAL, 90, 85, 10, -1, "May lower opponent's Accuracy.", 30, 0, 3)
.attr(StatChangeAttr, BattleStat.ACC, -1)

View File

@ -521,7 +521,7 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
function getRandomPartyMemberFunc(speciesPool: Species[], postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
return (scene: BattleScene, level: integer) => {
const species = getPokemonSpecies(Phaser.Math.RND.pick(speciesPool)).getSpeciesForLevel(level, true);
const ret = new EnemyPokemon(scene, getPokemonSpecies(species), level);
const ret = new EnemyPokemon(scene, getPokemonSpecies(species), level, true);
if (postProcess)
postProcess(ret);
return ret;
@ -532,7 +532,7 @@ function getSpeciesFilterRandomPartyMemberFunc(speciesFilter: PokemonSpeciesFilt
const originalSpeciesFilter = speciesFilter;
speciesFilter = (species: PokemonSpecies) => allowLegendaries || (!species.legendary && !species.pseudoLegendary && !species.mythical) && originalSpeciesFilter(species);
return (scene: BattleScene, level: integer) => {
const ret = new EnemyPokemon(scene, scene.randomSpecies(scene.currentBattle.waveIndex, level, speciesFilter), level);
const ret = new EnemyPokemon(scene, scene.randomSpecies(scene.currentBattle.waveIndex, level, speciesFilter), level, true);
if (postProcess)
postProcess(ret);
return ret;
@ -735,7 +735,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT ]))
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.setSpeciesFilter(species => species.baseTotal >= 540)
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.RAYQUAZA ])),
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.RAYQUAZA ], p => p.formIndex = 0)),
[TrainerType.RIVAL_6]: new TrainerConfig(++t).setBoss().setStaticParty().setEncounterBgm('final').setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_6)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT ]))

View File

@ -660,6 +660,9 @@ export const modifierTypes = {
GOLDEN_POKEBALL: () => new ModifierType(`Golden ${getPokeballName(PokeballType.POKEBALL)}`, 'Adds 1 extra item option at the end of every battle',
(type, _args) => new Modifiers.ExtraModifierModifier(type), 'pb_gold', null, 'pb_bounce_1'),
ENEMY_DAMAGE_BOOSTER: () => new ModifierType('Damage Booster', 'Increases damage by 20%', (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type), 'wl_item_drop'),
ENEMY_DAMAGE_REDUCTION: () => new ModifierType('Damage Reducer', 'Reduces incoming damage by 10%', (type, _args) => new Modifiers.EnemyDamageReducerModifier(type), 'wl_guard_spec')
};
const modifierPool = {
@ -797,6 +800,13 @@ const trainerModifierPool = {
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
};
export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType {
const modifierType = modifierTypeFunc();
if (!modifierType.id)
modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc);
return modifierType;
}
let modifierPoolThresholds = {};
let ignoredPoolIndexes = {};
@ -813,8 +823,8 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
let i = 0;
pool[t].reduce((total: integer, modifierType: WeightedModifierType) => {
const weightedModifierType = modifierType as WeightedModifierType;
const existingModifier = party[0].scene.findModifier(m => (m.type.generatorId || m.type.id) === weightedModifierType.modifierType.id);
const weight = !existingModifier || existingModifier.stackCount < existingModifier.getMaxStackCount()
const existingModifiers = party[0].scene.findModifiers(m => (m.type.generatorId || m.type.id) === weightedModifierType.modifierType.id, player);
const weight = !existingModifiers.length || existingModifiers.filter(m => m.stackCount < m.getMaxStackCount())
? weightedModifierType.weight instanceof Function
? (weightedModifierType.weight as Function)(party)
: weightedModifierType.weight as integer
@ -858,13 +868,10 @@ export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: i
return options;
}
export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER): PokemonHeldItemModifierType[] {
export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, gameMode: GameMode): PokemonHeldItemModifierType[] {
const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType).type as PokemonHeldItemModifierType);
if (waveIndex === 200) {
const miniBlackHole = modifierTypes.MINI_BLACK_HOLE();
miniBlackHole.id = 'MINI_BLACK_HOLE';
ret.push(miniBlackHole);
}
if ((gameMode === GameMode.CLASSIC && waveIndex === 200) || !(waveIndex % 1000))
ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType);
return ret;
}

View File

@ -1,5 +1,5 @@
import * as ModifierTypes from './modifier-type';
import { LearnMovePhase, LevelUpPhase, PokemonHealPhase } from "../battle-phases";
import { LearnMovePhase, LevelUpPhase, ObtainStatusEffectPhase, PokemonHealPhase } from "../battle-phases";
import BattleScene from "../battle-scene";
import { getLevelTotalExp } from "../data/exp";
import { PokeballType } from "../data/pokeball";
@ -15,6 +15,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
import { BerryType, getBerryEffectFunc, getBerryPredicate } from '../data/berry';
import { Species } from '../data/species';
import { BattleType } from '../battle';
import { StatusEffect } from '../data/status-effect';
type ModifierType = ModifierTypes.ModifierType;
export type ModifierPredicate = (modifier: Modifier) => boolean;
@ -671,13 +672,13 @@ export class PreserveBerryModifier extends PersistentModifier {
apply(args: any[]): boolean {
if (!(args[0] as Utils.BooleanHolder).value)
(args[0] as Utils.BooleanHolder).value = this.getStackCount() === this.getMaxStackCount() || Utils.randInt(this.getMaxStackCount()) < this.getStackCount();
(args[0] as Utils.BooleanHolder).value = Utils.randInt(this.getMaxStackCount()) < this.getStackCount();
return true;
}
getMaxStackCount(): integer {
return 4;
return 3;
}
}
@ -1224,4 +1225,88 @@ export class ExtraModifierModifier extends PersistentModifier {
getMaxStackCount(): integer {
return 3;
}
}
export abstract class EnemyPersistentModifer extends PersistentModifier {
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
}
export class EnemyDamageBoosterModifier extends EnemyPersistentModifer {
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
match(modifier: Modifier): boolean {
return modifier instanceof EnemyDamageBoosterModifier;
}
clone(): EnemyDamageBoosterModifier {
return new EnemyDamageBoosterModifier(this.type, this.stackCount);
}
apply(args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + 0.2 * this.getStackCount()));
return true;
}
getMaxStackCount(): number {
return 5;
}
}
export class EnemyDamageReducerModifier extends EnemyPersistentModifer {
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
match(modifier: Modifier): boolean {
return modifier instanceof EnemyDamageReducerModifier;
}
clone(): EnemyDamageReducerModifier {
return new EnemyDamageReducerModifier(this.type, this.stackCount);
}
apply(args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 - 0.2 * this.getStackCount()));
return true;
}
getMaxStackCount(): number {
return 5;
}
}
export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifer {
public effect: StatusEffect;
constructor(type: ModifierType, effect: StatusEffect, stackCount?: integer) {
super(type, stackCount);
this.effect = effect;
}
match(modifier: Modifier): boolean {
return modifier instanceof EnemyAttackStatusEffectChanceModifier && modifier.effect === this.effect;
}
clone(): EnemyDamageReducerModifier {
return new EnemyAttackStatusEffectChanceModifier(this.type, this.effect, this.stackCount);
}
apply(args: any[]): boolean {
const target = (args[0] as Pokemon);
if (Utils.randInt(10) < this.getStackCount())
target.scene.unshiftPhase(new ObtainStatusEffectPhase(target.scene, target.getBattlerIndex(), this.effect));
return true;
}
getMaxStackCount(): number {
return 5;
}
}

View File

@ -8,7 +8,7 @@ import * as Utils from './utils';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier } from './data/type';
import { getLevelTotalExp } from './data/exp';
import { Stat } from './data/pokemon-stat';
import { AttackTypeBoosterModifier, PokemonBaseStatModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier';
import { AttackTypeBoosterModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, PokemonBaseStatModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier';
import { PokeballType } from './data/pokeball';
import { Gender } from './data/gender';
import { initMoveAnim, loadMoveAnimAssets } from './data/battle-anims';
@ -125,23 +125,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
}
const rand1 = Utils.binToDec(Utils.decToBin(this.id).substring(0, 16));
const rand2 = Utils.binToDec(Utils.decToBin(this.id).substring(16, 32));
const E = this.scene.gameData.trainerId ^ this.scene.gameData.secretId;
const F = rand1 ^ rand2;
if (this.shiny === undefined) {
let shinyThreshold = new Utils.IntegerHolder(32);
this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
console.log(shinyThreshold.value);
this.shiny = (E ^ F) < shinyThreshold.value;
if ((E ^ F) < 32)
console.log('REAL SHINY!!');
if (this.shiny)
console.log((E ^ F), shinyThreshold.value);
}
if (this.shiny === undefined)
this.trySetShiny();
this.winCount = 0;
this.pokerus = false;
@ -207,6 +192,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
abstract isPlayer(): boolean;
abstract hasTrainer(): boolean;
abstract getFieldIndex(): integer;
abstract getBattlerIndex(): BattlerIndex;
@ -507,6 +494,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.summonData.moveset[moveIndex] = move;
}
trySetShiny(): boolean {
const rand1 = Utils.binToDec(Utils.decToBin(this.id).substring(0, 16));
const rand2 = Utils.binToDec(Utils.decToBin(this.id).substring(16, 32));
const E = this.scene.gameData.trainerId ^ this.scene.gameData.secretId;
const F = rand1 ^ rand2;
let shinyThreshold = new Utils.IntegerHolder(32);
if (!this.hasTrainer()) {
this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
console.log(shinyThreshold.value);
}
this.shiny = (E ^ F) < shinyThreshold.value;
if ((E ^ F) < 32)
console.log('REAL SHINY!!');
return this.shiny;
}
generateAndPopulateMoveset(): void {
this.moveset = [];
const movePool = [];
@ -629,7 +636,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
let result: HitResult;
const move = battlerMove.getMove();
const moveCategory = move.category;
let damage = 0;
let damage = new Utils.NumberHolder(0);
const cancelled = new Utils.BooleanHolder(false);
const typeless = !!move.getAttrs(TypelessAttr).length
@ -683,19 +690,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
if (!isTypeImmune) {
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
damage = Math.floor(damage / 2);
damage.value = Math.floor(damage.value / 2);
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
if (this.getTag(hta.tagType))
damage *= 2;
damage.value *= 2;
});
}
const fixedDamage = new Utils.IntegerHolder(0);
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
if (!isTypeImmune && fixedDamage.value) {
damage = fixedDamage.value;
damage.value = fixedDamage.value;
isCritical = false;
result = HitResult.EFFECTIVE;
}
@ -713,15 +720,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
result = HitResult.NO_EFFECT;
}
if (!source.isPlayer())
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage);
if (!this.isPlayer())
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
if (damage) {
this.scene.unshiftPhase(new DamagePhase(this.scene, this.getBattlerIndex(), result as DamageResult));
if (isCritical)
this.scene.queueMessage('A critical hit!');
this.scene.setPhaseQueueSplice();
damage = Math.min(damage, this.hp);
this.damage(damage);
source.turnData.damageDealt += damage;
this.turnData.attacksReceived.unshift({ move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id });
damage.value = Math.min(damage.value, this.hp);
this.damage(damage.value);
source.turnData.damageDealt += damage.value;
this.turnData.attacksReceived.unshift({ move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id });
}
switch (result) {
@ -1097,6 +1110,10 @@ export class PlayerPokemon extends Pokemon {
return true;
}
hasTrainer(): boolean {
return true;
}
getFieldIndex(): integer {
return this.scene.getPlayerField().indexOf(this);
}
@ -1165,12 +1182,15 @@ export class PlayerPokemon extends Pokemon {
}
export class EnemyPokemon extends Pokemon {
public trainer: boolean;
public aiType: AiType;
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, dataSource?: PokemonData) {
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainer: boolean, dataSource?: PokemonData) {
super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource ? dataSource.formIndex : scene.arena.getFormIndex(species),
dataSource?.gender, dataSource?.shiny, dataSource);
this.trainer = trainer;
if (!dataSource) {
let prevolution: Species;
let speciesId = species.speciesId;
@ -1323,6 +1343,10 @@ export class EnemyPokemon extends Pokemon {
return false;
}
hasTrainer(): boolean {
return this.trainer;
}
getFieldIndex(): integer {
return this.scene.getEnemyField().indexOf(this);
}

View File

@ -253,7 +253,7 @@ export class GameData {
scene.newArena(sessionData.arena.biome, true);
sessionData.enemyParty.forEach((enemyData, e) => {
const enemyPokemon = enemyData.toPokemon(scene) as EnemyPokemon;
const enemyPokemon = enemyData.toPokemon(scene, battleType) as EnemyPokemon;
battle.enemyParty[e] = enemyPokemon;
if (battleType === BattleType.WILD)
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
@ -274,7 +274,7 @@ export class GameData {
}
for (let enemyModifierData of sessionData.enemyModifiers) {
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]) as PokemonHeldItemModifier;
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]);
if (modifier)
scene.addEnemyModifier(modifier);
}

View File

@ -1,3 +1,4 @@
import { BattleType } from "../battle";
import BattleScene from "../battle-scene";
import { Gender } from "../data/gender";
import { PokeballType } from "../data/pokeball";
@ -69,10 +70,10 @@ export default class PokemonData {
}
}
toPokemon(scene: BattleScene): Pokemon {
toPokemon(scene: BattleScene, battleType?: BattleType): Pokemon {
const species = getPokemonSpecies(this.species);
if (this.player)
return new PlayerPokemon(scene, species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this);
return new EnemyPokemon(scene, species, this.level, this);
return new EnemyPokemon(scene, species, this.level, battleType === BattleType.TRAINER, this);
}
}

View File

@ -131,7 +131,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
? getPokemonSpecies(battle.enemyParty[offset].species.getSpeciesForLevel(level))
: this.genNewPartyMemberSpecies(level);
ret = new EnemyPokemon(this.scene, species, level);
ret = new EnemyPokemon(this.scene, species, level, true);
}, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + ((index + 1) << 8));
return ret;
@ -151,7 +151,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
tier--;
}
const tierPool = this.config.speciesPools[tier];
ret = getPokemonSpecies(getPokemonSpecies(Phaser.Math.RND.pick(tierPool)).getSpeciesForLevel(level));
ret = getPokemonSpecies(getPokemonSpecies(Phaser.Math.RND.pick(tierPool)).getSpeciesForLevel(level, true));
} else
ret = getPokemonSpecies(this.scene.randomSpecies(battle.waveIndex, level, this.config.speciesFilter).getSpeciesForLevel(level));

View File

@ -60,13 +60,13 @@ export default class BallUiHandler extends UiHandler {
let success = false;
const pokeballTypeCount = Object.keys(this.scene.currentBattle.turnPokeballCounts).length;
const pokeballTypeCount = Object.keys(this.scene.pokeballCounts).length;
if (button === Button.ACTION || button === Button.CANCEL) {
const commandPhase = this.scene.getCurrentPhase() as CommandPhase;
success = true;
if (button === Button.ACTION && this.cursor < pokeballTypeCount) {
if (this.scene.currentBattle.turnPokeballCounts[this.cursor]) {
if (this.scene.pokeballCounts[this.cursor]) {
if (commandPhase.handleCommand(Command.BALL, this.cursor)) {
this.scene.ui.setMode(Mode.COMMAND, commandPhase.getFieldIndex());
this.scene.ui.setMode(Mode.MESSAGE);
@ -94,7 +94,7 @@ export default class BallUiHandler extends UiHandler {
}
updateCounts() {
this.countsText.setText(Object.values(this.scene.currentBattle.turnPokeballCounts).map(c => `x${c}`).join('\n'));
this.countsText.setText(Object.values(this.scene.pokeballCounts).map(c => `x${c}`).join('\n'));
}
setCursor(cursor: integer): boolean {