From 06b782cd06a3f89a0481afbf78dbbe9427c87a5f Mon Sep 17 00:00:00 2001 From: RedstonewolfX <108761527+RedstonewolfX@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:44:03 -0400 Subject: [PATCH] Log item transfers --- src/battle-scene.ts | 1 + src/logger.ts | 117 ++++++++++++++++++++------ src/phases.ts | 33 ++++---- src/system/settings/settings.ts | 22 ++++- src/ui/fight-ui-handler.ts | 140 ++++++++++++++++++++++++++++++-- src/ui/party-ui-handler.ts | 4 +- 6 files changed, 263 insertions(+), 54 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 131cb0c91b6..331b06c57f4 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -120,6 +120,7 @@ export default class BattleScene extends SceneBase { public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1"; public enableMoveInfo: boolean = true; public enableRetries: boolean = false; + public damageDisplay: string = "Off"; /** * Determines the condition for a notification should be shown for Candy Upgrades * - 0 = 'Off' diff --git a/src/logger.ts b/src/logger.ts index d72dd36cb36..04d546c3832 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -16,6 +16,8 @@ import { Item } from "pokenode-ts"; import Trainer from "./field/trainer"; import { Species } from "./enums/species"; import { junit } from "node:test/reporters"; +import { i } from "vitest/dist/reporters-xEmem8D4.js"; +import { GameModes } from "./game-mode"; /** * All logs. @@ -38,6 +40,33 @@ export const logKeys: string[] = [ "d", // Debug ]; +export function getLogID(scene: BattleScene) { + return "drpd_log:" + scene.seed +} +export function getLogs() { + while(logs.length > 0) + logs.pop() + for (var i = 0; i < localStorage.length; i++) { + if (localStorage.key(i).substring(0, 9) == "drpd_log:") { + logs.push(["drpd.json", localStorage.key(i), localStorage.key(i).substring(9), "", "", ""]) + } + } +} +export function getMode(scene: BattleScene) { + switch (scene.gameMode.modeId) { + case GameModes.CLASSIC: + return "Classic" + case GameModes.ENDLESS: + return "Endless" + case GameModes.SPLICED_ENDLESS: + return "Spliced Endless" + case GameModes.DAILY: + return "Daily" + case GameModes.CHALLENGE: + return "Challenge" + } +} + export const rarities = [] export const rarityslot = [0] @@ -226,16 +255,23 @@ export function getSize(str: string) { return d.toString() + filesizes[unit] } -export function generateOption(i: integer): OptionSelectItem { +export function generateOption(i: integer, saves: any): OptionSelectItem { var filename: string = (JSON.parse(localStorage.getItem(logs[i][1])) as DRPD).title var op: OptionSelectItem = { - label: ` Export ${filename} (${getSize(printDRPD("", "", JSON.parse(localStorage.getItem(logs[i][1])) as DRPD))})`, + label: `Export ${filename} (${getSize(printDRPD("", "", JSON.parse(localStorage.getItem(logs[i][1])) as DRPD))})`, handler: () => { downloadLogByID(i) return false; } } + for (var j = 0; j < saves.length; j++) { + console.log(saves[j].seed, logs[i][2], saves[j].seed == logs[i][2]) + if (saves[j].seed == logs[i][2]) { + op.label = "[Slot " + (j + 1) + "]" + op.label.substring(6) + } + } if (logs[i][4] != "") { + op.label = " " + op.label op.item = logs[i][4] } return op; @@ -306,6 +342,7 @@ export function downloadLogByID(i: integer) { export function logTeam(scene: BattleScene, floor: integer = undefined) { if (floor == undefined) floor = scene.currentBattle.waveIndex var team = scene.getEnemyParty() + console.log("Log Enemy Team") if (team[0].hasTrainer()) { //var sprite = scene.currentBattle.trainer.config.getSpriteKey() //var trainerCat = Utils.getEnumKeys(TrainerType)[Utils.getEnumValues(TrainerType).indexOf(scene.currentBattle.trainer.config.trainerType)] @@ -320,20 +357,47 @@ export function logTeam(scene: BattleScene, floor: integer = undefined) { } } export function logActions(scene: BattleScene, floor: integer, action: string) { - if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) - var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; - var wv: Wave = getWave(drpd, floor, scene) - wv.actions.push(action) - console.log(drpd) - localStorage.setItem("drpd", JSON.stringify(drpd)) + if (localStorage.getItem(getLogID(scene)) == null) localStorage.setItem(getLogID(scene), JSON.stringify(newDocument(getMode(scene) + " Run"))) + var drpd: DRPD = JSON.parse(localStorage.getItem(getLogID(scene))) as DRPD; + console.log("Log Action", drpd) + var wv: Wave = getWave(drpd, floor, scene) + wv.actions.push(action) + console.log(drpd) + localStorage.setItem(getLogID(scene), JSON.stringify(drpd)) +} +export function logShop(scene: BattleScene, floor: integer, action: string) { + if (localStorage.getItem(getLogID(scene)) == null) localStorage.setItem(getLogID(scene), JSON.stringify(newDocument(getMode(scene) + " Run"))) + var drpd: DRPD = JSON.parse(localStorage.getItem(getLogID(scene))) as DRPD; + console.log("Log Shop Item", drpd) + var wv: Wave = getWave(drpd, floor, scene) + wv.shop = action + console.log(drpd) + localStorage.setItem(getLogID(scene), JSON.stringify(drpd)) } export function getWave(drpd: DRPD, floor: integer, scene: BattleScene) { var wv: Wave; var insertPos: integer; + console.log(drpd.waves) + if (drpd.waves[floor - 1] != undefined) { + return drpd.waves[floor - 1] + } + drpd.waves[floor - 1] = { + id: floor, + reload: false, + //type: floor % 10 == 0 ? "boss" : (floor % 10 == 5 ? "trainer" : "wild"), + type: floor % 10 == 0 ? "boss" : "wild", + double: scene.currentBattle.double, + actions: [], + shop: "", + biome: getBiomeName(scene.arena.biomeType), + pokemon: [] + } + return drpd.waves[floor - 1] for (var i = 0; i < drpd.waves.length; i++) { - if (drpd.waves[i] != undefined) { + if (drpd.waves[i] != undefined && drpd.waves[i] != null) { if (drpd.waves[i].id == floor) { wv = drpd.waves[i] + console.log("Found wave for floor " + floor + " at index " + i) if (wv.pokemon == undefined) wv.pokemon = [] } } else if (insertPos == undefined) { @@ -341,6 +405,7 @@ export function getWave(drpd: DRPD, floor: integer, scene: BattleScene) { } } if (wv == undefined && insertPos != undefined) { + console.log("Created new wave for floor " + floor + " at index " + insertPos) drpd.waves[insertPos] = { id: floor, reload: false, @@ -392,9 +457,10 @@ export function getWave(drpd: DRPD, floor: integer, scene: BattleScene) { console.error("Go yell at @redstonewolf8557 to fix this") } else { for (var i = 0; i < drpd.waves.length; i++) { - if (drpd.waves[i] != undefined) { + if (drpd.waves[i] != undefined && drpd.waves[i] != null) { if (drpd.waves[i].id == floor) { wv = drpd.waves[i] + console.log("Found wave for floor " + floor + " at index " + i) if (wv.pokemon == undefined) wv.pokemon = [] } } else if (insertPos == undefined) { @@ -402,6 +468,7 @@ export function getWave(drpd: DRPD, floor: integer, scene: BattleScene) { } } if (wv == undefined && insertPos != undefined) { + console.log("Created new wave for floor " + floor + " at index " + insertPos) drpd.waves[insertPos] = { id: floor, reload: false, @@ -479,8 +546,9 @@ export function logPokemon(scene: BattleScene, floor: integer = undefined, slot: setRow("e", newLine, floor, slot) //console.log(localStorage.getItem(logs[logKeys.indexOf("e")][1]).split("\n")) */ - if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) - var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; + if (localStorage.getItem(getLogID(scene)) == null) localStorage.setItem(getLogID(scene), JSON.stringify(newDocument(getMode(scene) + " Run"))) + var drpd: DRPD = JSON.parse(localStorage.getItem(getLogID(scene))) as DRPD; + console.log("Log Enemy Pokemon", drpd) var wv: Wave = getWave(drpd, floor, scene) var pk: PokeData = exportPokemon(pokemon, encounterRarity) if (wv.pokemon[slot] != undefined) { @@ -537,36 +605,33 @@ export function logPokemon(scene: BattleScene, floor: integer = undefined, slot: } if (pk.rarity == undefined) pk.rarity = "[Unknown]" wv.pokemon[slot] = pk; + while (wv.actions.length > 0) + wv.actions.pop() console.log(drpd) - localStorage.setItem("drpd", JSON.stringify(drpd)) -} -export function resetWave(scene: BattleScene, floor: integer = undefined) { - if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) - var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; - drpd.waves[floor] = exportWave(scene) - console.log(drpd) - localStorage.setItem("drpd", JSON.stringify(drpd)) + localStorage.setItem(getLogID(scene), JSON.stringify(drpd)) } export function logTrainer(scene: BattleScene, floor: integer = undefined) { - if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) - var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; + if (localStorage.getItem(getLogID(scene)) == null) localStorage.setItem(getLogID(scene), JSON.stringify(newDocument(getMode(scene) + " Run"))) + var drpd: DRPD = JSON.parse(localStorage.getItem(getLogID(scene))) as DRPD; + console.log("Log Trainer", drpd) var wv: Wave = getWave(drpd, floor, scene) var t: TrainerData = exportTrainer(scene.currentBattle.trainer) wv.trainer = t wv.type = "trainer" console.log(drpd) - localStorage.setItem("drpd", JSON.stringify(drpd)) + localStorage.setItem(getLogID(scene), JSON.stringify(drpd)) } export function logPlayerTeam(scene: BattleScene) { - if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) - var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; + if (localStorage.getItem(getLogID(scene)) == null) localStorage.setItem(getLogID(scene), JSON.stringify(newDocument(getMode(scene) + " Run"))) + var drpd: DRPD = JSON.parse(localStorage.getItem(getLogID(scene))) as DRPD; //var wv: Wave = getWave(drpd, 1, scene) + console.log("Log Player Starters", drpd) var P = scene.getParty() for (var i = 0; i < P.length; i++) { drpd.starters[i] = exportPokemon(P[i]) } console.log(drpd) - localStorage.setItem("drpd", JSON.stringify(drpd)) + localStorage.setItem(getLogID(scene), JSON.stringify(drpd)) } export function dataSorter(a: string, b: string) { diff --git a/src/phases.ts b/src/phases.ts index 5e17e68c6a3..b95a86614dd 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -409,9 +409,10 @@ export class TitlePhase extends Phase { logMenu(): boolean { const options: OptionSelectItem[] = []; + LoggerTools.getLogs() for (var i = 0; i < LoggerTools.logs.length; i++) { if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { - options.push(LoggerTools.generateOption(i) as OptionSelectItem) + options.push(LoggerTools.generateOption(i, this.getSaves()) as OptionSelectItem) } else { //options.push(LoggerTools.generateAddOption(i, this.scene, this)) } @@ -1324,7 +1325,7 @@ export class EncounterPhase extends BattlePhase { doEncounterCommon(showEncounterMessage: boolean = true) { const enemyField = this.scene.getEnemyField(); - LoggerTools.resetWave(this.scene, this.scene.currentBattle.waveIndex) + //LoggerTools.resetWave(this.scene, this.scene.currentBattle.waveIndex) LoggerTools.logTeam(this.scene, this.scene.currentBattle.waveIndex) if (this.scene.getEnemyParty()[0].hasTrainer()) { LoggerTools.logTrainer(this.scene, this.scene.currentBattle.waveIndex) @@ -2743,20 +2744,6 @@ export class TurnStartPhase extends FieldPhase { if (!queuedMove) { continue; } - const move = pokemon.getMoveset().find(m => m.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); - if (!this.scene.currentBattle.double) { - playerActions.push(move.getName()) - } else { - var T = this.getBattlers(pokemon) - for (var i = 0; i < T.length; i++) { - if (T[i] == undefined) { - T.splice(i, 1) - i-- - } - } - console.log(queuedMove.targets, T, queuedMove.targets.map(p => T[p+1].name)) - playerActions.push(move.getName() + " → " + queuedMove.targets.map(p => T[p+1].name).join(", ")) - } break; case Command.BALL: var ballNames = [ @@ -4502,11 +4489,13 @@ export class VictoryPhase extends PokemonPhase { if (this.scene.currentBattle.waveIndex % 10) { this.scene.pushPhase(new SelectModifierPhase(this.scene)); } else if (this.scene.gameMode.isDaily) { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM)); if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); } } else { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") const superExpWave = !this.scene.gameMode.isEndless ? (this.scene.offsetGym ? 0 : 20) : 10; if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex === 10) { this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_SHARE)); @@ -4524,6 +4513,7 @@ export class VictoryPhase extends PokemonPhase { } this.scene.pushPhase(new NewBattlePhase(this.scene)); } else { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") this.scene.currentBattle.battleType = BattleType.CLEAR; this.scene.score += this.scene.gameMode.getClearScoreBonus(); this.scene.updateScoreText(); @@ -5612,6 +5602,7 @@ export class AttemptRunPhase extends PokemonPhase { if (playerPokemon.randSeedInt(256) < escapeChance.value) { this.scene.playSound("flee"); + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); this.scene.tweens.add({ @@ -5672,6 +5663,7 @@ export class SelectModifierPhase extends BattlePhase { if (rowCursor < 0 || cursor < 0) { this.scene.ui.showText(i18next.t("battle:skipItemQuestion"), null, () => { this.scene.ui.setOverlayMode(Mode.CONFIRM, () => { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") this.scene.ui.revertMode(); this.scene.ui.setMode(Mode.MESSAGE); super.end(); @@ -5691,6 +5683,7 @@ export class SelectModifierPhase extends BattlePhase { return false; } else { this.scene.reroll = true; + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Reroll" + (this.scene.lockModifierTiers ? " (Locked)" : "")) this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type.tier))); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); @@ -5701,11 +5694,17 @@ export class SelectModifierPhase extends BattlePhase { } break; case 1: - this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => { + this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer, isAll: boolean, isFirst: boolean) => { if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; const itemModifier = itemModifiers[itemIndex]; + if (isAll) { + if (isFirst) + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Transfer [" + (fromSlotIndex + 1) + "] " + this.scene.getParty()[fromSlotIndex].name + " (All) → [" + (toSlotIndex + 1) + "] " + this.scene.getParty()[toSlotIndex].name) + } else { + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Transfer [" + (fromSlotIndex + 1) + "] " + this.scene.getParty()[fromSlotIndex].name + " (" + itemModifier.type.name + (itemQuantity == itemModifier.getStackCount() ? "" : " x" + itemQuantity) + ") → [" + (toSlotIndex + 1) + "] " + this.scene.getParty()[toSlotIndex].name) + } this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity); } else { this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index b09de095259..920df6b1063 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -43,7 +43,8 @@ const AUTO_DISABLED: SettingOption[] = [ export enum SettingType { GENERAL, DISPLAY, - AUDIO + AUDIO, + MOD } type SettingOption = { @@ -98,6 +99,7 @@ export const SettingKeys = { SE_Volume: "SE_VOLUME", Music_Preference: "MUSIC_PREFERENCE", Show_BGM_Bar: "SHOW_BGM_BAR", + Damage_Display: "DAMAGE_DISPLAY" }; /** @@ -242,6 +244,22 @@ export const Setting: Array = [ default: 0, type: SettingType.GENERAL }, + { + key: SettingKeys.Damage_Display, + label: "Damage Display", + options: [{ + label: "Off", + value: "Off" + }, { + label: "Value", + value: "Value" + }, { + label: "Percent", + value: "Percent" + }], + default: 0, + type: SettingType.GENERAL + }, { key: SettingKeys.Tutorials, label: i18next.t("settings:tutorials"), @@ -604,6 +622,8 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Enable_Retries: scene.enableRetries = Setting[index].options[value].value === "On"; break; + case SettingKeys.Damage_Display: + scene.damageDisplay = Setting[index].options[value].value case SettingKeys.Skip_Seen_Dialogues: scene.skipSeenDialogues = Setting[index].options[value].value === "On"; break; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index d7f2ae81f77..147142eacc4 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -15,15 +15,16 @@ import { Stat } from "#app/data/pokemon-stat.js"; import { Abilities } from "#app/enums/abilities.js"; import { WeatherType } from "#app/data/weather.js"; import { Moves } from "#app/enums/moves.js"; -import { AddSecondStrikeAbAttr, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, ConditionalCritAbAttr, DamageBoostAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPriorityMoveImmunityAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, MultCritAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, TypeImmunityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr } from "#app/data/ability.js"; +import { AddSecondStrikeAbAttr, AllyMoveCategoryPowerBoostAbAttr, AlwaysHitAbAttr, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, ConditionalCritAbAttr, DamageBoostAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentEvasionAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, MultCritAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, TypeImmunityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "#app/data/ability.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from "#app/data/arena-tag.js"; -import { BattlerTagLapseType, HelpingHandTag, TypeBoostTag } from "#app/data/battler-tags.js"; +import { BattlerTagLapseType, HelpingHandTag, SemiInvulnerableTag, TypeBoostTag } from "#app/data/battler-tags.js"; import { TerrainType } from "#app/data/terrain.js"; -import { AttackTypeBoosterModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, PokemonMultiHitModifier, TempBattleStatBoosterModifier } from "#app/modifier/modifier.js"; +import { AttackTypeBoosterModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, TempBattleStatBoosterModifier } from "#app/modifier/modifier.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { TempBattleStat } from "#app/data/temp-battle-stat.js"; import { StatusEffect } from "#app/data/status-effect.js"; +import { BattleStat } from "#app/data/battle-stat.js"; export default class FightUiHandler extends UiHandler { private movesContainer: Phaser.GameObjects.Container; @@ -439,6 +440,117 @@ export default class FightUiHandler extends UiHandler { return [damage1.value, damage2.value] } + calculateAccuracy(user: Pokemon, target: Pokemon, move: PokemonMove) { + if (this.scene.currentBattle.double && false) { + switch (move.getMove().moveTarget) { + case MoveData.MoveTarget.USER: // Targets yourself + return -1; // Moves targeting yourself always hit + case MoveData.MoveTarget.OTHER: // Targets one Pokemon + return move.getMove().accuracy + case MoveData.MoveTarget.ALL_OTHERS: // Targets all Pokemon + return move.getMove().accuracy; + case MoveData.MoveTarget.NEAR_OTHER: // Targets a Pokemon adjacent to the user + return move.getMove().accuracy; + case MoveData.MoveTarget.ALL_NEAR_OTHERS: // Targets all Pokemon adjacent to the user + return move.getMove().accuracy; + case MoveData.MoveTarget.NEAR_ENEMY: // Targets an opponent adjacent to the user + return move.getMove().accuracy; + case MoveData.MoveTarget.ALL_NEAR_ENEMIES: // Targets all opponents adjacent to the user + return move.getMove().accuracy; + case MoveData.MoveTarget.RANDOM_NEAR_ENEMY: // Targets a random opponent adjacent to the user + return move.getMove().accuracy; + case MoveData.MoveTarget.ALL_ENEMIES: // Targets all opponents + return move.getMove().accuracy; + case MoveData.MoveTarget.ATTACKER: // Counter move + return move.getMove().accuracy; + case MoveData.MoveTarget.NEAR_ALLY: // Targets an adjacent ally + return move.getMove().accuracy; + case MoveData.MoveTarget.ALLY: // Targets an ally + return move.getMove().accuracy; + case MoveData.MoveTarget.USER_OR_NEAR_ALLY: // Targets an ally or yourself + return move.getMove().accuracy; + case MoveData.MoveTarget.USER_AND_ALLIES: // Targets all on your side + return move.getMove().accuracy; + case MoveData.MoveTarget.ALL: // Targets everyone + return move.getMove().accuracy; + case MoveData.MoveTarget.USER_SIDE: // Targets your field + return move.getMove().accuracy; + case MoveData.MoveTarget.ENEMY_SIDE: // Targets enemy field + return -1; // Moves placing entry hazards always hit + case MoveData.MoveTarget.BOTH_SIDES: // Targets the entire field + return move.getMove().accuracy; + case MoveData.MoveTarget.PARTY: // Targets all of the Player's Pokemon, including ones that aren't active + return move.getMove().accuracy; + case MoveData.MoveTarget.CURSE: + return move.getMove().accuracy; + } + } + // Moves targeting the user and entry hazards can't miss + if ([MoveData.MoveTarget.USER, MoveData.MoveTarget.ENEMY_SIDE].includes(move.getMove().moveTarget)) { + return -1; + } + if (target == undefined) return move.getMove().accuracy; + // If either Pokemon has No Guard, + if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) { + return -1; + } + // If the user should ignore accuracy on a target, check who the user targeted last turn and see if they match + if (user.getTag(BattlerTagType.IGNORE_ACCURACY) && (user.getLastXMoves().slice(1).find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1) { + return -1; + } + + const hiddenTag = target.getTag(SemiInvulnerableTag); + if (hiddenTag && !move.getMove().getAttrs(MoveData.HitsTagAttr).some(hta => hta.tagType === hiddenTag.tagType)) { + return 0; + } + const moveAccuracy = new Utils.NumberHolder(move.getMove().accuracy); + + MoveData.applyMoveAttrs(MoveData.VariableAccuracyAttr, user, target, move.getMove(), moveAccuracy); + applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, move.getMove(), { value: false }, moveAccuracy); + + if (moveAccuracy.value === -1) { + return -1; + } + + const isOhko = move.getMove().hasAttr(MoveData.OneHitKOAccuracyAttr); + + if (!isOhko) { + user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy); + } + + if (this.scene.arena.weather?.weatherType === WeatherType.FOG) { + moveAccuracy.value = Math.floor(moveAccuracy.value * 0.9); + } + + if (!isOhko && this.scene.arena.getTag(ArenaTagType.GRAVITY)) { + moveAccuracy.value = Math.floor(moveAccuracy.value * 1.67); + } + + const userAccuracyLevel = new Utils.IntegerHolder(user.summonData.battleStats[BattleStat.ACC]); + const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, user, null, targetEvasionLevel); + applyAbAttrs(IgnoreOpponentEvasionAbAttr, user, null, targetEvasionLevel); + MoveData.applyMoveAttrs(MoveData.IgnoreOpponentStatChangesAttr, user, target, move.getMove(), targetEvasionLevel); + this.scene.applyModifiers(TempBattleStatBoosterModifier, user.isPlayer(), TempBattleStat.ACC, userAccuracyLevel); + + const accuracyMultiplier = new Utils.NumberHolder(1); + if (userAccuracyLevel.value !== targetEvasionLevel.value) { + accuracyMultiplier.value = userAccuracyLevel.value > targetEvasionLevel.value + ? (3 + Math.min(userAccuracyLevel.value - targetEvasionLevel.value, 6)) / 3 + : 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6)); + } + + applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, user, BattleStat.ACC, accuracyMultiplier, move.getMove()); + + const evasionMultiplier = new Utils.NumberHolder(1); + applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier); + + accuracyMultiplier.value /= evasionMultiplier.value; + + return moveAccuracy.value * accuracyMultiplier.value + } + calcDamage(scene: BattleScene, user: PlayerPokemon, target: Pokemon, move: PokemonMove) { /* var power = move.getMove().power @@ -535,11 +647,17 @@ export default class FightUiHandler extends UiHandler { var percentChance = (target.hp - dmgLow + 1) / (dmgHigh - dmgLow + 1) koText = " (" + Math.round(percentChance * 100) + "% KO)" } - return (Math.round(dmgLow) == Math.round(dmgHigh) ? Math.round(dmgLow).toString() : Math.round(dmgLow) + "-" + Math.round(dmgHigh)) + koText + if (target.getMoveEffectiveness(user, move) == undefined) { + return "---" + } + if (scene.damageDisplay == "Value") + return target.getMoveEffectiveness(user, move) + "x - " + (Math.round(dmgLow) == Math.round(dmgHigh) ? Math.round(dmgLow).toString() : Math.round(dmgLow) + "-" + Math.round(dmgHigh)) + koText dmgLow = Math.round((dmgLow)/target.getBattleStat(Stat.HP)*100) dmgHigh = Math.round((dmgHigh)/target.getBattleStat(Stat.HP)*100) - return (dmgLow == dmgHigh ? dmgLow + "%" : dmgLow + "%-" + dmgHigh + "%") + koText - return "???" + if (scene.damageDisplay == "Percent") + return target.getMoveEffectiveness(user, move) + "x - " + (dmgLow == dmgHigh ? dmgLow + "%" : dmgLow + "%-" + dmgHigh + "%") + koText + if (scene.damageDisplay == "Off") + return target.getMoveEffectiveness(user, move) + "x" } setCursor(cursor: integer): boolean { @@ -574,9 +692,15 @@ export default class FightUiHandler extends UiHandler { const maxPP = pokemonMove.getMovePp(); const pp = maxPP - pokemonMove.ppUsed; + const accuracy1 = this.calculateAccuracy(pokemon, this.scene.getEnemyField()[0], pokemonMove) + const accuracy2 = this.calculateAccuracy(pokemon, this.scene.getEnemyField()[1], pokemonMove) + this.ppText.setText(`${Utils.padInt(pp, 2, " ")}/${Utils.padInt(maxPP, 2, " ")}`); this.powerText.setText(`${power >= 0 ? power : "---"}`); this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`); + this.accuracyText.setText(`${accuracy1 >= 0 ? accuracy1 : "---"}`); + if (this.scene.getEnemyField()[1] != undefined) + this.accuracyText.setText(`${accuracy1 >= 0 ? accuracy1 : "---"}/${accuracy2 >= 0 ? accuracy2 : "---"}`); const ppPercentLeft = pp / maxPP; @@ -939,8 +1063,8 @@ export function simulateAttack(scene: BattleScene, user: Pokemon, target: Pokemo applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage1); applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage2); - console.log("damage (min)", damage1.value, move.name, power.value, sourceAtk, targetDef); - console.log("damage (max)", damage2.value, move.name, power.value, sourceAtkCrit, targetDefCrit); + //console.log("damage (min)", damage1.value, move.name, power.value, sourceAtk, targetDef); + //console.log("damage (max)", damage2.value, move.name, power.value, sourceAtkCrit, targetDefCrit); // In case of fatal damage, this tag would have gotten cleared before we could lapse it. const destinyTag = target.getTag(BattlerTagType.DESTINY_BOND); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index e820c8cb0d2..8a89722dca3 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -63,7 +63,7 @@ export enum PartyOption { } export type PartySelectCallback = (cursor: integer, option: PartyOption) => void; -export type PartyModifierTransferSelectCallback = (fromCursor: integer, index: integer, itemQuantity?: integer, toCursor?: integer) => void; +export type PartyModifierTransferSelectCallback = (fromCursor: integer, index: integer, itemQuantity?: integer, toCursor?: integer, isAll?: boolean, isFirst?: boolean) => void; export type PartyModifierSpliceSelectCallback = (fromCursor: integer, toCursor?: integer) => void; export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string; export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => string; @@ -326,7 +326,7 @@ export default class PartyUiHandler extends MessageUiHandler { if (option === PartyOption.TRANSFER) { if (this.transferCursor !== this.cursor) { if (this.transferAll) { - getTransferrableItemsFromPokemon(this.scene.getParty()[this.transferCursor]).forEach((_, i) => (this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, i, this.transferQuantitiesMax[i], this.cursor)); + getTransferrableItemsFromPokemon(this.scene.getParty()[this.transferCursor]).forEach((_, i) => (this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, i, this.transferQuantitiesMax[i], this.cursor, true, i == 0)); } else { (this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, this.transferOptionCursor, this.transferQuantities[this.transferOptionCursor], this.cursor); }