battle-scence annotation

This commit is contained in:
J.Javier9434 2025-01-13 19:13:13 +09:00
parent 90c8c97437
commit b5e8e42f40
6 changed files with 11708 additions and 136 deletions

6037
dependency-graph.dot Normal file

File diff suppressed because it is too large Load Diff

BIN
dependency-graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

5289
dependency-result.txt Normal file

File diff suppressed because it is too large Load Diff

139
package-lock.json generated
View File

@ -29,7 +29,7 @@
"@typescript-eslint/eslint-plugin": "^8.0.0-alpha.54",
"@typescript-eslint/parser": "^8.0.0-alpha.54",
"@vitest/coverage-istanbul": "^2.0.4",
"dependency-cruiser": "^16.3.10",
"dependency-cruiser": "^16.9.0",
"eslint": "^9.7.0",
"eslint-plugin-import-x": "^4.2.1",
"inquirer": "^11.0.2",
@ -2257,9 +2257,9 @@
}
},
"node_modules/acorn": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@ -2299,9 +2299,9 @@
}
},
"node_modules/acorn-walk": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
"integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
"dependencies": {
"acorn": "^8.11.0"
@ -2691,11 +2691,10 @@
}
},
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz",
"integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
@ -2869,34 +2868,33 @@
}
},
"node_modules/dependency-cruiser": {
"version": "16.3.10",
"resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-16.3.10.tgz",
"integrity": "sha512-WkCnibHBfvaiaQ+S46LZ6h4AR6oj42Vsf5/0Vgtrwdwn7ZekMJdZ/ALoTwNp/RaGlKW+MbV/fhSZOvmhAWVWzQ==",
"version": "16.9.0",
"resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-16.9.0.tgz",
"integrity": "sha512-Gc/xHNOBq1nk5i7FPCuexCD0m2OXB/WEfiSHfNYQaQaHZiZltnl5Ixp/ZG38Jvi8aEhKBQTHV4Aw6gmR7rWlOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "8.12.1",
"acorn-jsx": "5.3.2",
"acorn-jsx-walk": "2.0.0",
"acorn-loose": "8.4.0",
"acorn-walk": "8.3.3",
"ajv": "8.17.1",
"commander": "12.1.0",
"enhanced-resolve": "5.17.1",
"ignore": "5.3.1",
"acorn": "^8.14.0",
"acorn-jsx": "^5.3.2",
"acorn-jsx-walk": "^2.0.0",
"acorn-loose": "^8.4.0",
"acorn-walk": "^8.3.4",
"ajv": "^8.17.1",
"commander": "^13.0.0",
"enhanced-resolve": "^5.18.0",
"ignore": "^7.0.0",
"interpret": "^3.1.1",
"is-installed-globally": "1.0.0",
"json5": "2.2.3",
"memoize": "10.0.0",
"picocolors": "1.0.1",
"picomatch": "4.0.2",
"prompts": "2.4.2",
"is-installed-globally": "^1.0.0",
"json5": "^2.2.3",
"memoize": "^10.0.0",
"picocolors": "^1.1.1",
"picomatch": "^4.0.2",
"prompts": "^2.4.2",
"rechoir": "^0.8.0",
"safe-regex": "2.1.1",
"safe-regex": "^2.1.1",
"semver": "^7.6.3",
"teamcity-service-messages": "0.1.14",
"tsconfig-paths-webpack-plugin": "4.1.0",
"watskeburt": "4.1.0"
"teamcity-service-messages": "^0.1.14",
"tsconfig-paths-webpack-plugin": "^4.2.0",
"watskeburt": "^4.2.2"
},
"bin": {
"depcruise": "bin/dependency-cruise.mjs",
@ -2910,6 +2908,15 @@
"node": "^18.17||>=20"
}
},
"node_modules/dependency-cruiser/node_modules/ignore": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.0.tgz",
"integrity": "sha512-lcX8PNQygAa22u/0BysEY8VhaFRzlOkvdlKczDPnJvrkJD1EuqzEky5VYYKM2iySIuaVIDv9N190DfSreSLw2A==",
"dev": true,
"engines": {
"node": ">= 4"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -2957,11 +2964,10 @@
"license": "MIT"
},
"node_modules/enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@ -3836,8 +3842,7 @@
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
"dev": true
},
"node_modules/graphemer": {
"version": "1.4.0",
@ -4933,7 +4938,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -5454,9 +5458,9 @@
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/picomatch": {
@ -5509,12 +5513,6 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss/node_modules/picocolors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"dev": true
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -6101,7 +6099,6 @@
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
@ -6169,7 +6166,6 @@
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
@ -6359,27 +6355,11 @@
}
}
},
"node_modules/tsconfig-paths-webpack-plugin": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz",
"integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.0",
"enhanced-resolve": "^5.7.0",
"tsconfig-paths": "^4.1.2"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": {
"node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
@ -6389,6 +6369,21 @@
"node": ">=6"
}
},
"node_modules/tsconfig-paths-webpack-plugin": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz",
"integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==",
"dev": true,
"dependencies": {
"chalk": "^4.1.0",
"enhanced-resolve": "^5.7.0",
"tapable": "^2.2.1",
"tsconfig-paths": "^4.1.2"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
@ -6754,9 +6749,9 @@
}
},
"node_modules/watskeburt": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-4.1.0.tgz",
"integrity": "sha512-KkY5H51ajqy9HYYI+u9SIURcWnqeVVhdH0I+ab6aXPGHfZYxgRCwnR6Lm3+TYB6jJVt5jFqw4GAKmwf1zHmGQw==",
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-4.2.2.tgz",
"integrity": "sha512-AOCg1UYxWpiHW1tUwqpJau8vzarZYTtzl2uu99UptBmbzx6kOzCGMfRLF6KIRX4PYekmryn89MzxlRNkL66YyA==",
"dev": true,
"bin": {
"watskeburt": "dist/run-cli.js"

View File

@ -31,7 +31,7 @@
"@typescript-eslint/eslint-plugin": "^8.0.0-alpha.54",
"@typescript-eslint/parser": "^8.0.0-alpha.54",
"@vitest/coverage-istanbul": "^2.0.4",
"dependency-cruiser": "^16.3.10",
"dependency-cruiser": "^16.9.0",
"eslint": "^9.7.0",
"eslint-plugin-import-x": "^4.2.1",
"inquirer": "^11.0.2",

View File

@ -397,6 +397,7 @@ export default class BattleScene extends SceneBase {
this.scene.remove(LoadingScene.KEY);
initGameSpeed.apply(this);
this.inputController = new InputsController(this);
/// 컨트롤러 초기화
this.uiInputs = new UiInputs(this, this.inputController);
this.gameData = new GameData(this);
@ -410,7 +411,7 @@ export default class BattleScene extends SceneBase {
this.fieldSpritePipeline = new FieldSpritePipeline(this.game);
(this.renderer as Phaser.Renderer.WebGL.WebGLRenderer).pipelines.add("FieldSprite", this.fieldSpritePipeline);
///배틀 시작
this.launchBattle();
}
@ -430,13 +431,13 @@ export default class BattleScene extends SceneBase {
a.setOrigin(0);
a.setSize(320, 240);
});
/// 전투 영역과 관련 게임 오브젝트를 담고 있으며, 6배로 확장됩니다.
const field = this.add.container(0, 0);
field.setName("field");
field.setScale(6);
this.field = field;
/// UI 요소를 담고 있으며, 필드 아래에 위치하고 깊이(Depth)는 1로 설정됩니다.
const fieldUI = this.add.container(0, this.game.canvas.height);
fieldUI.setName("field-ui");
fieldUI.setDepth(1);
@ -470,7 +471,7 @@ export default class BattleScene extends SceneBase {
uiContainer.setScale(6);
this.uiContainer = uiContainer;
///fieldOverlay와 shopOverlay라는 두 개의 사각형이 fieldUI에 추가되어 특정 상황(예: 상점 상호작용)에서 UI 오버레이로 사용됩니다.
const overlayWidth = this.game.canvas.width / 6;
const overlayHeight = (this.game.canvas.height / 6) - 48;
this.fieldOverlay = this.add.rectangle(0, overlayHeight * -1 - 48, overlayWidth, overlayHeight, 0x424242);
@ -484,6 +485,8 @@ export default class BattleScene extends SceneBase {
this.shopOverlay.setOrigin(0, 0);
this.shopOverlay.setAlpha(0);
this.fieldUI.add(this.shopOverlay);
///플레이어와 적을 위한 modifierBar와 enemyModifierBar가 초기화되어 UI 컨테이너(uiContainer)에 추가됩니다.
this.modifiers = [];
this.enemyModifiers = [];
@ -497,13 +500,13 @@ export default class BattleScene extends SceneBase {
this.enemyModifierBar.setName("enemy-modifier-bar");
this.add.existing(this.enemyModifierBar);
uiContainer.add(this.enemyModifierBar);
///플레이어 캐릭터를 나타내는 charSprite가 생성되어 fieldUI에 추가됩니다.
this.charSprite = new CharSprite(this);
this.charSprite.setName("sprite-char");
this.charSprite.setup();
this.fieldUI.add(this.charSprite);
///플레이어와 적의 포켓볼을 표시하는 pbTray와 pbTrayEnemy가 각각 초기화됩니다.
this.pbTray = new PokeballTray(this, true);
this.pbTray.setName("pb-tray");
this.pbTray.setup();
@ -576,7 +579,10 @@ export default class BattleScene extends SceneBase {
this.party = [];
const loadPokemonAssets = [];
/* arenaPlayer, arenaPlayerTransition, arenaEnemy, arenaNextEnemy ArenaBase .
, .
addFieldSprite .
*/
this.arenaPlayer = new ArenaBase(this, true);
this.arenaPlayer.setName("arena-player");
this.arenaPlayerTransition = new ArenaBase(this, true);
@ -623,7 +629,7 @@ export default class BattleScene extends SceneBase {
});
this.reset(false, false, true);
///ui 초기화
const ui = new UI(this);
this.uiContainer.add(ui);
@ -632,20 +638,28 @@ export default class BattleScene extends SceneBase {
ui.setup();
const defaultMoves = [ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ];
/*
initCommonAnims: 공통 .
initMoveAnim: 특정 .
loadMoveAnimAssets: 기본 .
*/
Promise.all([
Promise.all(loadPokemonAssets),
initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)),
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)),
this.initStarterColors()
]).then(() => {
/*
LoginPhase: 플레이어 .
TitlePhase: 게임 .
*/
this.pushPhase(new LoginPhase(this));
this.pushPhase(new TitlePhase(this));
///shiftPhase를 호출하여 첫 번째 단계를 시작
this.shiftPhase();
});
}
///세션 초기화
initSession(): void {
if (this.sessionPlayTime === null) {
this.sessionPlayTime = 0;
@ -678,7 +692,7 @@ export default class BattleScene extends SceneBase {
this.updateMoneyText();
this.updateScoreText();
}
///경험치 이미지 초기화
async initExpSprites(): Promise<void> {
if (expSpriteKeys.length) {
return;
@ -690,7 +704,7 @@ export default class BattleScene extends SceneBase {
Promise.resolve();
});
}
///포켓몬 변형(variant) 데이터 초기화
async initVariantData(): Promise<void> {
Object.keys(variantData).forEach(key => delete variantData[key]);
await this.cachedFetch("./images/pokemon/variant/_masterlist.json").then(res => res.json())
@ -719,7 +733,9 @@ export default class BattleScene extends SceneBase {
Promise.resolve();
});
}
/*
fetch API를 URL에 . .
*/
cachedFetch(url: string, init?: RequestInit): Promise<Response> {
const manifest = this.game["manifest"];
if (manifest) {
@ -730,7 +746,18 @@ export default class BattleScene extends SceneBase {
}
return fetch(url, init);
}
/*
starterColors . starter-colors.json , .
:
starterColors가 resolve를 .
cachedFetch를 JSON .
:
starterColors를 .
(sc) starterColors에 .
resolve를 Promise를 .
*/
initStarterColors(): Promise<void> {
return new Promise(resolve => {
if (starterColors) {
@ -769,7 +796,8 @@ export default class BattleScene extends SceneBase {
});
});
}
/*
*/
hasExpSprite(key: string): boolean {
const keyMatch = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/g.exec(key);
if (!keyMatch) {
@ -794,7 +822,7 @@ export default class BattleScene extends SceneBase {
}
return true;
}
///플레이어 파티 가져오는 함수
public getPlayerParty(): PlayerPokemon[] {
return this.party;
}
@ -803,6 +831,7 @@ export default class BattleScene extends SceneBase {
* @returns An array of {@linkcode PlayerPokemon} filtered from the player's party
* that are {@linkcode Pokemon.isAllowedInBattle | allowed in battle}.
*/
///이 메서드는 전투에 참여할 수 있는 플레이어의 포켓몬 목록을 반환
public getPokemonAllowedInBattle(): PlayerPokemon[] {
return this.getPlayerParty().filter(p => p.isAllowedInBattle());
}
@ -814,6 +843,7 @@ export default class BattleScene extends SceneBase {
* or `undefined` if there are no valid pokemon
* @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true`
*/
///는 첫 번째 활성 포켓몬을 반환하는 역할을 합니다
public getPlayerPokemon(includeSwitching: boolean = true): PlayerPokemon | undefined {
return this.getPlayerField().find(p => p.isActive() && (includeSwitching || p.switchOutStatus === false));
}
@ -823,36 +853,54 @@ export default class BattleScene extends SceneBase {
* Does not actually check if the pokemon are on the field or not.
* @returns array of {@linkcode PlayerPokemon}
*/
///상대 필드 위의 포켓몬
public getPlayerField(): PlayerPokemon[] {
const party = this.getPlayerParty();
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
}
/// 상대팀 파티 가져옴
public getEnemyParty(): EnemyPokemon[] {
return this.currentBattle?.enemyParty ?? [];
}
/**
* @returns The first {@linkcode EnemyPokemon} that is {@linkcode getEnemyField on the field}
* and {@linkcode EnemyPokemon.isActive is active}
* (aka {@linkcode EnemyPokemon.isAllowedInBattle is allowed in battle}),
* or `undefined` if there are no valid pokemon
* @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true`
* Retrieves the first active enemy Pokémon on the field.
* If `includeSwitching` is `true`, it includes Pokémon currently switching out.
*
* @param includeSwitching - Whether to include Pokémon that are switching out, default is `true`.
* @returns The first active enemy Pokémon on the field that is allowed in battle,
* or `undefined` if there are no valid Pokémon.
*/
public getEnemyPokemon(includeSwitching: boolean = true): EnemyPokemon | undefined {
return this.getEnemyField().find(p => p.isActive() && (includeSwitching || p.switchOutStatus === false));
}
/**
* Returns an array of EnemyPokemon of length 1 or 2 depending on if in a double battle or not.
* Does not actually check if the pokemon are on the field or not.
* @returns array of {@linkcode EnemyPokemon}
* Retrieves an array of enemy Pokémon for the current battle.
* The length of the returned array is either 1 (single battle) or 2 (double battle),
* depending on the current battle mode.
*
* This method does not check if the Pokémon are actually on the field.
*
* @returns An array of enemy Pokémon, with a length of 1 or 2 depending on the battle mode.
*/
public getEnemyField(): EnemyPokemon[] {
const party = this.getEnemyParty();
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
}
/**
* Retrieves all Pokémon currently on the field, including both player and enemy Pokémon.
* The returned array has a fixed length of 4, with the following structure:
* - [0, 1]: Player's Pokémon.
* - [2, 3]: Enemy's Pokémon.
*
* If `activeOnly` is `true`, it filters out any Pokémon that are not active.
*
* @param activeOnly - Whether to include only active Pokémon, default is `false`.
* @returns An array of Pokémon on the field, optionally filtered by active status.
*/
public getField(activeOnly: boolean = false): Pokemon[] {
const ret = new Array(4).fill(null);
const playerField = this.getPlayerField();
@ -864,11 +912,13 @@ export default class BattleScene extends SceneBase {
: ret;
}
/**
* Used in doubles battles to redirect moves from one pokemon to another when one faints or is removed from the field
* @param removedPokemon {@linkcode Pokemon} the pokemon that is being removed from the field (flee, faint), moves to be redirected FROM
* @param allyPokemon {@linkcode Pokemon} the pokemon that will have the moves be redirected TO
*/
/// 더블배틀에서 하나 죽으면 옆엣놈으로 이전하는 함수
redirectPokemonMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void {
// failsafe: if not a double battle just return
if (this.currentBattle.double === false) {
@ -886,9 +936,12 @@ export default class BattleScene extends SceneBase {
}
/**
* Returns the ModifierBar of this scene, which is declared private and therefore not accessible elsewhere
* @param isEnemy Whether to return the enemy's modifier bar
* @returns {ModifierBar}
* Returns the `ModifierBar` of the current scene.
* If `isEnemy` is true, it returns the enemy's modifier bar (`enemyModifierBar`),
* otherwise it returns the player's modifier bar (`modifierBar`).
*
* @param isEnemy - Optional boolean to determine whether to return the enemy's modifier bar.
* @returns {ModifierBar} The appropriate `ModifierBar` (either player's or enemy's).
*/
getModifierBar(isEnemy?: boolean): ModifierBar {
return isEnemy ? this.enemyModifierBar : this.modifierBar;
@ -904,12 +957,47 @@ export default class BattleScene extends SceneBase {
return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles;
}
/**
* Retrieves a Pokémon from either the player's party or the enemy's party
* by its unique `pokemonId`.
*
* @param pokemonId - The unique ID of the Pokémon to search for.
* @returns {Pokemon | null} The Pokémon with the specified ID, or `null` if not found.
*/
getPokemonById(pokemonId: integer): Pokemon | null {
const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId);
return (findInParty(this.getPlayerParty()) || findInParty(this.getEnemyParty())) ?? null;
}
addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon {
/**
* Adds a new Pokémon to the player's party.
*
* @param species - The species of the Pokémon.
* @param level - The level of the Pokémon.
* @param abilityIndex - (Optional) The index of the Pokémon's ability.
* @param formIndex - (Optional) The form index of the Pokémon.
* @param gender - (Optional) The gender of the Pokémon.
* @param shiny - (Optional) Whether the Pokémon is shiny.
* @param variant - (Optional) The variant of the Pokémon.
* @param ivs - (Optional) An array of individual values (IVs) for the Pokémon's stats.
* @param nature - (Optional) The nature of the Pokémon.
* @param dataSource - (Optional) Data source for initializing the Pokémon.
* @param postProcess - (Optional) A callback function for post-processing the Pokémon.
* @returns {PlayerPokemon} The newly created `PlayerPokemon` instance.
*/
addPlayerPokemon(
species: PokemonSpecies,
level: integer,
abilityIndex?: integer,
formIndex?: integer,
gender?: Gender,
shiny?: boolean,
variant?: Variant,
ivs?: integer[],
nature?: Nature,
dataSource?: Pokemon | PokemonData,
postProcess?: (playerPokemon: PlayerPokemon) => void
): PlayerPokemon {
const pokemon = new PlayerPokemon(this, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource);
if (postProcess) {
postProcess(pokemon);
@ -918,6 +1006,19 @@ export default class BattleScene extends SceneBase {
return pokemon;
}
/**
* Creates and adds a new enemy Pokémon to the battle.
* Overrides level and species if applicable, and applies special logic for boss Pokémon.
*
* @param species - The species of the Pokémon.
* @param level - The level of the Pokémon.
* @param trainerSlot - The slot assigned to the trainer.
* @param boss - (Optional) Whether the Pokémon is a boss, default is `false`.
* @param shinyLock - (Optional) Whether the Pokémon is shiny-locked, default is `false`.
* @param dataSource - (Optional) Data source for initializing the Pokémon.
* @param postProcess - (Optional) A callback function for further processing of the Pokémon.
* @returns {EnemyPokemon} The newly created `EnemyPokemon` instance.
*/
addEnemyPokemon(species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean = false, shinyLock: boolean = false, dataSource?: PokemonData, postProcess?: (enemyPokemon: EnemyPokemon) => void): EnemyPokemon {
if (Overrides.OPP_LEVEL_OVERRIDE > 0) {
level = Overrides.OPP_LEVEL_OVERRIDE;
@ -953,13 +1054,13 @@ export default class BattleScene extends SceneBase {
pokemon.init();
return pokemon;
}
/**
* Removes a {@linkcode PlayerPokemon} from the party, and clears modifiers for that Pokemon's id
* Useful for MEs/Challenges that remove Pokemon from the player party temporarily or permanently
* @param pokemon
* @param destroy Default true. If true, will destroy the {@linkcode PlayerPokemon} after removing
*/
* Removes a player Pokémon from the party and clears associated modifiers.
* This is useful for events or challenges where Pokémon may be temporarily or permanently removed.
*
* @param pokemon - The `PlayerPokemon` instance to be removed.
* @param destroy - Whether to destroy the Pokémon object after removal, default is `true`.
*/
removePokemonFromPlayerParty(pokemon: PlayerPokemon, destroy: boolean = true) {
if (!pokemon) {
return;
@ -973,7 +1074,13 @@ export default class BattleScene extends SceneBase {
}
this.updateModifiers(true);
}
/**
* Removes a player Pokémon from the party and clears associated modifiers.
* This is useful for events or challenges where Pokémon may be temporarily or permanently removed.
*
* @param pokemon - The `PlayerPokemon` instance to be removed.
* @param destroy - Whether to destroy the Pokémon object after removal, default is `true`.
*/
addPokemonIcon(pokemon: Pokemon, x: number, y: number, originX: number = 0.5, originY: number = 0.5, ignoreOverride: boolean = false): Phaser.GameObjects.Container {
const container = this.add.container(x, y);
container.setName(`${pokemon.name}-icon`);
@ -1052,7 +1159,7 @@ export default class BattleScene extends SceneBase {
return container;
}
///뭔가 시드 설정
setSeed(seed: string): void {
this.seed = seed;
this.rngCounter = 0;
@ -1184,14 +1291,35 @@ export default class BattleScene extends SceneBase {
});
}
}
/**
* Calculates the chance of a double battle occurring in the specified wave.
*
* The base chance is determined by whether the wave index is a multiple of 10.
* - If the wave index is a multiple of 10, the base chance is set to 32.
* - Otherwise, the base chance is set to 8.
*
* Modifiers and abilities are applied to adjust the base chance.
*
* @param newWaveIndex - The index of the new wave.
* @param playerField - An array of `PlayerPokemon` instances currently on the field.
* @returns {number} The calculated chance of a double battle, with a minimum value of 1.
*/
getDoubleBattleChance(newWaveIndex: number, playerField: PlayerPokemon[]) {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance));
return Math.max(doubleChance.value, 1);
}
/**
* Initializes a new battle based on the given wave index, battle type, and other parameters.
*
* @param waveIndex - (Optional) The wave index for the new battle. If not provided, it defaults to the next wave.
* @param battleType - (Optional) The type of battle (Trainer, Wild, Mystery Encounter).
* @param trainerData - (Optional) Predefined trainer data for the battle.
* @param double - (Optional) Whether the battle should be a double battle.
* @param mysteryEncounterType - (Optional) Specifies the type of mystery encounter, if applicable.
* @returns {Battle | null} The newly created `Battle` instance or `null` if initialization fails.
*/
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounterType?: MysteryEncounterType): Battle | null {
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
@ -1204,7 +1332,7 @@ export default class BattleScene extends SceneBase {
this.resetSeed(newWaveIndex);
const playerField = this.getPlayerField();
/// 고정배틀요소가 따로 있는듯
if (this.gameMode.isFixedBattle(newWaveIndex) && trainerData === undefined) {
battleConfig = this.gameMode.getFixedBattle(newWaveIndex);
newDouble = battleConfig.double;
@ -1221,7 +1349,7 @@ export default class BattleScene extends SceneBase {
} else {
newBattleType = battleType;
}
///트레이너 배틀일시
if (newBattleType === BattleType.TRAINER) {
const trainerType = this.arena.randomTrainerType(newWaveIndex);
let doubleTrainer = false;
@ -1358,7 +1486,7 @@ export default class BattleScene extends SceneBase {
for (const pokemon of this.getPlayerParty()) {
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger);
}
///바이옴 변경
if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
this.pushPhase(new NextEncounterPhase(this));
} else {
@ -1374,7 +1502,7 @@ export default class BattleScene extends SceneBase {
return this.currentBattle;
}
///바이옴 변경되면 새로운 데이터 설정
newArena(biome: Biome): Arena {
this.arena = new Arena(this, biome, Biome[biome].toLowerCase());
this.eventTarget.dispatchEvent(new NewArenaEvent());
@ -1383,7 +1511,7 @@ export default class BattleScene extends SceneBase {
return this.arena;
}
///필드 설정
updateFieldScale(): Promise<void> {
return new Promise(resolve => {
const fieldScale = Math.floor(Math.pow(1 / this.getField(true)
@ -1417,7 +1545,15 @@ export default class BattleScene extends SceneBase {
});
});
}
/**
* Determines the form index for a given Pokémon species based on various conditions.
*
* @param species - The species of the Pokémon.
* @param gender - (Optional) The gender of the Pokémon.
* @param nature - (Optional) The nature of the Pokémon.
* @param ignoreArena - (Optional) Whether to ignore arena-specific form changes.
* @returns {integer} The form index of the Pokémon.
*/
getSpeciesFormIndex(species: PokemonSpecies, gender?: Gender, nature?: Nature, ignoreArena?: boolean): integer {
if (!species.forms?.length) {
return 0;
@ -1502,7 +1638,13 @@ export default class BattleScene extends SceneBase {
return this.arena.getSpeciesFormIndex(species);
}
/**
* Generates a random boolean value indicating whether the gym offset is applied.
*
* The result is consistent for the same seed due to the use of `executeWithSeedOffset`.
*
* @returns {boolean} Whether the offset gym is generated.
*/
private getGeneratedOffsetGym(): boolean {
let ret = false;
this.executeWithSeedOffset(() => {
@ -1510,7 +1652,13 @@ export default class BattleScene extends SceneBase {
}, 0, this.seed.toString());
return ret;
}
/**
* Generates a random wave cycle offset between 0 and 35, in increments of 5.
*
* The result is consistent for the same seed due to the use of `executeWithSeedOffset`.
*
* @returns {integer} The generated wave cycle offset.
*/
private getGeneratedWaveCycleOffset(): integer {
let ret = 0;
this.executeWithSeedOffset(() => {
@ -1518,7 +1666,15 @@ export default class BattleScene extends SceneBase {
}, 0, this.seed.toString());
return ret;
}
/**
* Determines the number of health segments for a boss Pokémon encounter.
*
* @param waveIndex - The wave index of the current battle.
* @param level - The level of the enemy Pokémon.
* @param species - (Optional) The species of the enemy Pokémon.
* @param forceBoss - (Optional) Whether to force the Pokémon to be treated as a boss.
* @returns {integer} The number of health segments for the boss encounter.
*/
getEncounterBossSegments(waveIndex: integer, level: integer, species?: PokemonSpecies, forceBoss: boolean = false): integer {
if (Overrides.OPP_HEALTH_SEGMENTS_OVERRIDE > 1) {
return Overrides.OPP_HEALTH_SEGMENTS_OVERRIDE;
@ -1812,7 +1968,7 @@ export default class BattleScene extends SceneBase {
this.fieldUI.sendToBack(this.moneyText);
this.fieldUI.sendToBack(this.scoreText);
}
///기절한 적 점수?
addFaintedEnemyScore(enemy: EnemyPokemon): void {
let scoreIncrease = enemy.getSpeciesForm().getBaseExp() * (enemy.level / this.getMaxExpLevel()) * ((enemy.ivs.reduce((iv: integer, total: integer) => total += iv, 0) / 93) * 0.2 + 0.8);
this.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemy.id, false).map(m => scoreIncrease *= (m as PokemonHeldItemModifier).getScoreMultiplier());
@ -1821,7 +1977,7 @@ export default class BattleScene extends SceneBase {
}
this.currentBattle.battleScore += Math.ceil(scoreIncrease);
}
/// 최대 레벨 계산
getMaxExpLevel(ignoreLevelCap?: boolean): integer {
if (ignoreLevelCap) {
return Number.MAX_SAFE_INTEGER;
@ -1846,7 +2002,7 @@ export default class BattleScene extends SceneBase {
})) ] : allSpecies.filter(s => s.isCatchable());
return filteredSpecies[Utils.randSeedInt(filteredSpecies.length)];
}
///랜덤바이옴 선택
generateRandomBiome(waveIndex: integer): Biome {
const relWave = waveIndex % 250;
const biomes = Utils.getEnumValues(Biome).slice(1, Utils.getEnumValues(Biome).filter(b => b >= 40).length * -1);
@ -2297,6 +2453,8 @@ export default class BattleScene extends SceneBase {
* @param {Phase} phase - The phase to be added to the conditional queue.
* @param {() => boolean} condition - A function that returns a boolean indicating whether the phase should be executed.
*
* This method allows deferring the execution of a phase until a specific condition is met.
Phases added through this method are stored in conditionalQueue, and they will only be executed if the corresponding condition function returns true.
*/
pushConditionalPhase(phase: Phase, condition: () => boolean): void {
this.conditionalQueue.push([ condition, phase ]);
@ -2306,6 +2464,9 @@ export default class BattleScene extends SceneBase {
* Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false
* @param phase {@linkcode Phase} the phase to add
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
* This method adds a phase to one of two different queues:
phaseQueue: The main phase queue where phases are executed in order.
nextCommandPhaseQueue: A queue for phases that should be executed later (deferred).
*/
pushPhase(phase: Phase, defer: boolean = false): void {
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
@ -2314,6 +2475,11 @@ export default class BattleScene extends SceneBase {
/**
* Adds Phase to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex
* @param phase {@linkcode Phase} the phase to add
*
* This method inserts a phase at the beginning of the phase queue (phaseQueuePrepend) or at a specific index.
If phaseQueuePrependSpliceIndex is -1, the phase is simply added to the end of phaseQueuePrepend.
If phaseQueuePrependSpliceIndex is set, the phase is inserted at the specified index.
Use Case: This is used for phases that need to be executed immediately or in a specific order (e.g., high-priority phases).
*/
unshiftPhase(phase: Phase): void {
if (this.phaseQueuePrependSpliceIndex === -1) {
@ -2516,7 +2682,7 @@ export default class BattleScene extends SceneBase {
}
this.phaseQueue.push(new TurnInitPhase(this));
}
///돈 더하기
addMoney(amount: integer): void {
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
this.updateMoneyText();
@ -2531,69 +2697,142 @@ export default class BattleScene extends SceneBase {
return Math.floor(moneyValue / 10) * 10;
}
addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean, cost?: number): Promise<boolean> {
/**
* Modifier() .
* - Modifier는 'PersistentModifier'() 'ConsumableModifier'() .
* - Promise<boolean> .
*
* @param modifier Modifier( null)
* @param ignoreUpdate
* @param playSound
* @param virtual (virtual이면 Modifier를 )
* @param instant
* @param cost ( )
* @returns Promise<boolean> -
*/
addModifier(
modifier: Modifier | null,
ignoreUpdate?: boolean,
playSound?: boolean,
virtual?: boolean,
instant?: boolean,
cost?: number
): Promise<boolean> {
// modifier가 null이면 즉시 false 반환
if (!modifier) {
return Promise.resolve(false);
}
// 비동기로 진행하는 Promise 생성
return new Promise(resolve => {
let success = false;
const soundName = modifier.type.soundName;
let success = false; // 처리 성공 여부를 추적
const soundName = modifier.type.soundName; // Modifier에 설정된 사운드 이름
// 업적(achievements) 관련 검증을 수행
this.validateAchvs(ModifierAchv, modifier);
// 제거가 필요한 Modifier 목록과, 후속 Promise 리스트 초기화
const modifiersToRemove: PersistentModifier[] = [];
const modifierPromises: Promise<boolean>[] = [];
// ──────────────────────────
// 1) PersistentModifier 처리
// ──────────────────────────
if (modifier instanceof PersistentModifier) {
// 테라스탈라이즈 Modifier는 이미 적용된 동일 PokemonId에 대한 테라스탈라이즈를 먼저 제거
if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId)));
modifiersToRemove.push(
...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId))
);
}
// 실제(virtual이 false)로 modifiers 배열에 add 성공 시에만 내부 로직 진행
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) {
// 폼체인지나 테라스탈라이즈의 경우, 해당 포켓몬 객체를 찾아서 즉시 effect를 apply
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
// apply 결과가 true면 success 갱신
success = modifier.apply(pokemon, true);
}
}
// 사운드를 재생해야 하고, 아직 로드되지 않은 사운드라면 playSound로 재생
if (playSound && !this.sound.get(soundName)) {
this.playSound(soundName);
}
} else if (!virtual) {
// PersistentModifier 추가가 실패했고(스택이 가득 참 등),
// virtual이 아니면 기본 tier에 대한 Modifier로 재시도
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
this.queueMessage(i18next.t("battle:itemStackFull", { fullItemName: modifier.type.name, itemName: defaultModifierType.name }), undefined, true);
return this.addModifier(defaultModifierType.newModifier(), ignoreUpdate, playSound, false, instant).then(success => resolve(success));
this.queueMessage(
i18next.t("battle:itemStackFull", {
fullItemName: modifier.type.name,
itemName: defaultModifierType.name
}),
undefined,
true
);
// 기본 Modifier를 새로 생성해서 다시 add 시도
return this.addModifier(
defaultModifierType.newModifier(),
ignoreUpdate,
playSound,
false,
instant
).then(success => resolve(success));
}
// 같은 종류(예: 테라스탈) Modifier가 중복되어 있으면 제거
for (const rm of modifiersToRemove) {
this.removeModifier(rm);
}
// ignoreUpdate가 아니면서 가상 적용이 아니면, Modifier 목록을 업데이트 후 결과 반환
if (!ignoreUpdate && !virtual) {
return this.updateModifiers(true, instant).then(() => resolve(success));
}
// ──────────────────────────
// 2) ConsumableModifier 처리
// ──────────────────────────
} else if (modifier instanceof ConsumableModifier) {
// 사운드가 설정되어 있고 playSound 옵션이 true라면 재생
if (playSound && !this.sound.get(soundName)) {
this.playSound(soundName);
}
// 포켓몬에 직접 적용하는 Consumable
if (modifier instanceof ConsumablePokemonModifier) {
// 파티에 있는 모든 포켓몬에게 일괄적으로 적용 시도
for (const p in this.party) {
const pokemon = this.party[p];
// Modifier별 추가 인자가 필요한 경우 준비
const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) {
// fainted(기절) 상태가 아닌지, 또는 부활시키는지에 따라 회복량을 계산
if (!modifier.fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
// HealingBoosterModifier가 걸려 있다면 회복 배율 증가
this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier);
args.push(hpRestoreMultiplier.value);
} else {
args.push(1);
args.push(1); // 기절 후 부활의 경우 기본 1배
}
} else if (modifier instanceof FusePokemonModifier) {
// FusePokemonModifier는 fusePokemonId가 필요
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
} else if (modifier instanceof RememberMoveModifier && !Utils.isNullOrUndefined(cost)) {
// RememberMoveModifier는 기술 재학습에 필요한 cost를 인자로 받음
args.push(cost);
}
// shouldApply로 실제 적용 가능성을 먼저 체크
if (modifier.shouldApply(pokemon, ...args)) {
const result = modifier.apply(pokemon, ...args);
// apply 결과가 Promise라면 비동기 처리 후에 success값을 반영
if (result instanceof Promise) {
modifierPromises.push(result.then(s => success ||= s));
} else {
@ -2602,11 +2841,18 @@ export default class BattleScene extends SceneBase {
}
}
return Promise.allSettled([ this.party.map(p => p.updateInfo(instant)), ...modifierPromises ]).then(() => resolve(success));
// 모든 포켓몬에 대해 적용이 끝나면, 파티 정보 갱신 후 Promise 전체 결과 반환
return Promise.allSettled([
this.party.map(p => p.updateInfo(instant)), // 각 포켓몬의 업데이트
...modifierPromises
]).then(() => resolve(success));
} else {
// 기타 ConsumableModifier (Party 전체가 아닌 Scene 자체에 적용되는 것 등)
const args = [ this ];
if (modifier.shouldApply(...args)) {
const result = modifier.apply(...args);
// apply가 비동기면 처리 완료 후 resolve
if (result instanceof Promise) {
return result.then(success => resolve(success));
} else {
@ -2616,10 +2862,12 @@ export default class BattleScene extends SceneBase {
}
}
// 이 시점까지 특별 처리에 해당되지 않으면, 성공 여부로 resolve
resolve(success);
});
}
///적 modifier 추가
addEnemyModifier(modifier: PersistentModifier, ignoreUpdate?: boolean, instant?: boolean): Promise<void> {
return new Promise(resolve => {
const modifiersToRemove: PersistentModifier[] = [];
@ -2720,7 +2968,7 @@ export default class BattleScene extends SceneBase {
});
});
}
///파티 멤버 modifier 제거
removePartyMemberModifiers(partyMemberIndex: integer): Promise<void> {
return new Promise(resolve => {
const pokemonId = this.getPlayerParty()[partyMemberIndex].id;
@ -2992,7 +3240,7 @@ export default class BattleScene extends SceneBase {
return null;
}
///뭔가 폼체인지
triggerPokemonFormChange(pokemon: Pokemon, formChangeTriggerType: Constructor<SpeciesFormChangeTrigger>, delayed: boolean = false, modal: boolean = false): boolean {
if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId)) {
@ -3096,6 +3344,9 @@ export default class BattleScene extends SceneBase {
modeChain: this.ui?.getModeChain() ?? [],
};
(window as any).gameInfo = gameInfo;
// Log the game info to the console
console.log("Game Info Updated:", gameInfo);
}
/**