From b228d488c2dabf908819a498b170754b1f352c27 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Sat, 17 Jul 2021 21:07:08 -0500 Subject: [PATCH] Start modularizing some battle files Progress is very incomplete, but this is mostly an experiment to establish this as possible at all. What's going on here is that we're using `remove-import-export` so the `import` and `export` statements just disappear after compiling (everything's still a global). This allows us to piecewise convert files to modules. So we're just using TypeScript to keep track of dependencies, and also to make it easier to use these files in other projects later on. I've tried to avoid circular dependencies, but there's one between `battle-animations` and `battle-animations-moves`. --- src/battle-animations-moves.ts | 4 +++- src/battle-animations.ts | 27 +++++++++++++++-------- src/battle-log.ts | 28 ++++++++++++++++-------- src/battle-scene-stub.ts | 12 ++++++---- src/battle-sound.ts | 4 ++-- src/battle.ts | 33 +++++++++++++++++----------- src/globals.d.ts | 40 +++++++++++++++------------------- tslint.json | 1 + 8 files changed, 88 insertions(+), 61 deletions(-) diff --git a/src/battle-animations-moves.ts b/src/battle-animations-moves.ts index ec39628e9..81c8a0681 100644 --- a/src/battle-animations-moves.ts +++ b/src/battle-animations-moves.ts @@ -9,7 +9,9 @@ * @license CC0-1.0 */ -const BattleMoveAnims: AnimTable = { +import {AnimTable, BattleOtherAnims} from './battle-animations'; + +export const BattleMoveAnims: AnimTable = { taunt: { anim(scene, [attacker, defender]) { BattleOtherAnims.dance.anim(scene, [attacker, defender]); diff --git a/src/battle-animations.ts b/src/battle-animations.ts index 396eb1934..b86378367 100644 --- a/src/battle-animations.ts +++ b/src/battle-animations.ts @@ -11,6 +11,12 @@ * @license MIT */ +import type {Battle, Pokemon, Side, WeatherState} from './battle'; +import type {BattleSceneStub} from './battle-scene-stub'; +import {BattleMoveAnims} from './battle-animations-moves'; +import {BattleLog} from './battle-log'; +import {BattleBGM, BattleSound} from './battle-sound'; + /* Most of this file is: CC0 (public domain) @@ -30,7 +36,7 @@ This license DOES NOT extend to any other files in this repository. */ -class BattleScene { +export class BattleScene implements BattleSceneStub { battle: Battle; animating = true; acceleration = 1; @@ -234,7 +240,7 @@ class BattleScene { } else { this.$frame.append('


'); this.$frame.find('div.playbutton button[name=play-muted]').click(() => { - this.battle.setMute(true); + this.setMute(true); this.battle.play(); }); } @@ -244,6 +250,9 @@ class BattleScene { this.$frame.find('div.playbutton').remove(); this.updateBgm(); } + setMute(muted: boolean) { + BattleSound.setMute(muted); + } wait(time: number) { if (!this.animating) return; this.timeOffset += time; @@ -589,7 +598,7 @@ class BattleScene { } else { let statustext = ''; if (pokemon.hp !== pokemon.maxhp) { - statustext += Pokemon.getHPText(pokemon); + statustext += pokemon.getHPText(); } if (pokemon.status) { if (statustext) statustext += '|'; @@ -1643,7 +1652,7 @@ class BattleScene { } } -interface ScenePos { +export interface ScenePos { /** - left, + right */ x?: number; /** - down, + up */ @@ -1669,7 +1678,7 @@ interface InitScenePos { display?: string; } -class Sprite { +export class Sprite { scene: BattleScene; $el: JQuery = null!; sp: SpriteData; @@ -1732,7 +1741,7 @@ class Sprite { } } -class PokemonSprite extends Sprite { +export class PokemonSprite extends Sprite { // HTML strings are constructed from this table and stored back in it to cache them protected static statusTable: {[id: string]: [string, 'good' | 'bad' | 'neutral'] | null | string} = { formechange: null, @@ -2795,7 +2804,7 @@ interface AnimData { prepareAnim?(scene: BattleScene, args: PokemonSprite[]): void; residualAnim?(scene: BattleScene, args: PokemonSprite[]): void; } -type AnimTable = {[k: string]: AnimData}; +export type AnimTable = {[k: string]: AnimData}; const BattleEffects: {[k: string]: SpriteData} = { wisp: { @@ -3112,7 +3121,7 @@ const BattleBackdrops = [ 'bg-skypillar.jpg', ]; -const BattleOtherAnims: AnimTable = { +export const BattleOtherAnims: AnimTable = { hitmark: { anim(scene, [attacker]) { scene.showEffect('hitmark', { @@ -5698,7 +5707,7 @@ const BattleOtherAnims: AnimTable = { }, }, }; -const BattleStatusAnims: AnimTable = { +export const BattleStatusAnims: AnimTable = { brn: { anim(scene, [attacker]) { scene.showEffect('fireball', { diff --git a/src/battle-log.ts b/src/battle-log.ts index da988d84c..283e81752 100644 --- a/src/battle-log.ts +++ b/src/battle-log.ts @@ -13,7 +13,17 @@ * @license MIT */ -class BattleLog { +import type {BattleScene} from './battle-animations'; + +// Caja +declare const html4: any; +declare const html: any; + +// defined in battle-log-misc +declare function MD5(input: string): string; +declare function formatText(input: string, isTrusted?: boolean): string; + +export class BattleLog { elem: HTMLDivElement; innerElem: HTMLDivElement; scene: BattleScene | null = null; @@ -358,7 +368,7 @@ class BattleLog { addSpacer() { this.addDiv('spacer battle-history', '
'); } - changeUhtml(id: string, html: string, forceAdd?: boolean) { + changeUhtml(id: string, htmlSrc: string, forceAdd?: boolean) { id = toID(id); const classContains = ' uhtml-' + id + ' '; let elements = [] as HTMLDivElement[]; @@ -374,9 +384,9 @@ class BattleLog { } } } - if (html && elements.length && !forceAdd) { + if (htmlSrc && elements.length && !forceAdd) { for (const element of elements) { - element.innerHTML = BattleLog.sanitizeHTML(html); + element.innerHTML = BattleLog.sanitizeHTML(htmlSrc); } this.updateScroll(); return; @@ -384,11 +394,11 @@ class BattleLog { for (const element of elements) { element.parentElement!.removeChild(element); } - if (!html) return; + if (!htmlSrc) return; if (forceAdd) { - this.addDiv('notice uhtml-' + id, BattleLog.sanitizeHTML(html)); + this.addDiv('notice uhtml-' + id, BattleLog.sanitizeHTML(htmlSrc)); } else { - this.prependDiv('notice uhtml-' + id, BattleLog.sanitizeHTML(html)); + this.prependDiv('notice uhtml-' + id, BattleLog.sanitizeHTML(htmlSrc)); } } hideChatFrom(userid: ID, showRevealButton = true, lineCount = 0) { @@ -634,8 +644,8 @@ class BattleLog { case 'uhtml': case 'uhtmlchange': let parts = target.split(','); - let html = parts.slice(1).join(',').trim(); - this.changeUhtml(parts[0], html, cmd === 'uhtml'); + let htmlSrc = parts.slice(1).join(',').trim(); + this.changeUhtml(parts[0], htmlSrc, cmd === 'uhtml'); return ['', '']; case 'raw': return ['chat', BattleLog.sanitizeHTML(target)]; diff --git a/src/battle-scene-stub.ts b/src/battle-scene-stub.ts index b27b7a74f..99a8dde09 100644 --- a/src/battle-scene-stub.ts +++ b/src/battle-scene-stub.ts @@ -1,4 +1,8 @@ -class BattleSceneStub { +import type {Pokemon, Side} from './battle'; +import type {ScenePos, PokemonSprite} from './battle-animations'; +import type {BattleLog} from './battle-log'; + +export class BattleSceneStub { animating: boolean = false; acceleration: number = NaN; gen: number = NaN; @@ -10,12 +14,12 @@ class BattleSceneStub { log: BattleLog = {add: (args: Args, kwargs?: KWArgs) => {}} as any; abilityActivateAnim(pokemon: Pokemon, result: string): void { } - addPokemonSprite(pokemon: Pokemon) { return null!; } + addPokemonSprite(pokemon: Pokemon): PokemonSprite { return null!; } addSideCondition(siden: number, id: ID, instant?: boolean | undefined): void { } animationOff(): void { } animationOn(): void { } maybeCloseMessagebar(args: Args, kwArgs: KWArgs): boolean { return false; } - closeMessagebar(): void { } + closeMessagebar(): boolean { return false; } damageAnim(pokemon: Pokemon, damage: string | number): void { } destroy(): void { } finishAnimations(): JQuery.Promise, any, any> | undefined { return void(0); } @@ -25,6 +29,7 @@ class BattleSceneStub { updateAcceleration(): void { } message(message: string, hiddenMessage?: string | undefined): void { } pause(): void { } + setMute(muted: boolean): void { } preemptCatchup(): void { } removeSideCondition(siden: number, id: ID): void { } reset(): void { } @@ -68,7 +73,6 @@ class BattleSceneStub { anim(pokemon: Pokemon, end: ScenePos, transition?: string) { } beforeMove(pokemon: Pokemon) { } afterMove(pokemon: Pokemon) { } - unlink(userid: string, showRevealButton = false) { } } if (typeof require === 'function') { diff --git a/src/battle-sound.ts b/src/battle-sound.ts index ae1bac2c2..5307e73f9 100644 --- a/src/battle-sound.ts +++ b/src/battle-sound.ts @@ -1,5 +1,5 @@ -class BattleBGM { +export class BattleBGM { /** * May be shared with other BGM objects: every battle has its own BattleBGM * object, but two battles with the same music will have the same HTMLAudioElement @@ -100,7 +100,7 @@ class BattleBGM { } } -const BattleSound = new class { +export const BattleSound = new class { soundCache: {[url: string]: HTMLAudioElement | undefined} = {}; bgm: BattleBGM[] = []; diff --git a/src/battle.ts b/src/battle.ts index 7e51d7a82..ceb23d287 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -27,13 +27,17 @@ * @license MIT */ -/** [id, element?, ...misc] */ -type EffectState = any[] & {0: ID}; -/** [name, minTimeLeft, maxTimeLeft] */ -type WeatherState = [string, number, number]; -type HPColor = 'r' | 'y' | 'g'; +import {BattleSceneStub} from './battle-scene-stub'; +import {BattleLog} from './battle-log'; +import {BattleScene, PokemonSprite, BattleStatusAnims} from './battle-animations'; -class Pokemon implements PokemonDetails, PokemonHealth { +/** [id, element?, ...misc] */ +export type EffectState = any[] & {0: ID}; +/** [name, minTimeLeft, maxTimeLeft] */ +export type WeatherState = [string, number, number]; +export type HPColor = 'r' | 'y' | 'g'; + +export class Pokemon implements PokemonDetails, PokemonHealth { name = ''; speciesForme = ''; @@ -570,6 +574,9 @@ class Pokemon implements PokemonDetails, PokemonHealth { } return percentage * maxWidth / 100; } + getHPText(precision = 1) { + return Pokemon.getHPText(this, precision); + } static getHPText(pokemon: PokemonHealth, precision = 1) { if (pokemon.maxhp === 100) return pokemon.hp + '%'; if (pokemon.maxhp !== 48) return (100 * pokemon.hp / pokemon.maxhp).toFixed(precision) + '%'; @@ -583,7 +590,7 @@ class Pokemon implements PokemonDetails, PokemonHealth { } } -class Side { +export class Side { battle: Battle; name = ''; id = ''; @@ -932,7 +939,7 @@ class Side { } } -interface PokemonDetails { +export interface PokemonDetails { details: string; name: string; speciesForme: string; @@ -942,14 +949,14 @@ interface PokemonDetails { ident: string; searchid: string; } -interface PokemonHealth { +export interface PokemonHealth { hp: number; maxhp: number; hpcolor: HPColor | ''; status: StatusName | 'tox' | '' | '???'; fainted?: boolean; } -interface ServerPokemon extends PokemonDetails, PokemonHealth { +export interface ServerPokemon extends PokemonDetails, PokemonHealth { ident: string; details: string; condition: string; @@ -976,8 +983,8 @@ interface ServerPokemon extends PokemonDetails, PokemonHealth { gigantamax: string | false; } -class Battle { - scene: BattleScene | BattleSceneStub; +export class Battle { + scene: BattleSceneStub; sidesSwitched = false; @@ -3669,7 +3676,7 @@ class Battle { } setMute(mute: boolean) { - BattleSound.setMute(mute); + this.scene.setMute(mute); } } diff --git a/src/globals.d.ts b/src/globals.d.ts index 67c82497e..b81224f6b 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -1,14 +1,5 @@ - - -// dependencies -/////////////// - -// Caja -declare var html4: any; -declare var html: any; - -// data -/////// +// dex data +/////////// declare var BattlePokedex: any; declare var BattleMovedex: any; @@ -16,21 +7,9 @@ declare var BattleAbilities: any; declare var BattleItems: any; declare var BattleAliases: any; declare var BattleStatuses: any; -// declare var BattleMoveAnims: any; -// declare var BattleStatusAnims: any; -// declare var BattleOtherAnims: any; -// declare var BattleBackdrops: any; -// declare var BattleBackdropsThree: any; -// declare var BattleBackdropsFour: any; -// declare var BattleBackdropsFive: any; -// declare var BattleEffects: any; declare var BattlePokemonSprites: any; declare var BattlePokemonSpritesBW: any; -// defined in battle-log-misc -declare function MD5(input: string): string; -declare function formatText(input: string, isTrusted?: boolean): string; - // PS globals ///////////// @@ -43,3 +22,18 @@ declare var app: {user: AnyObject, rooms: AnyObject, ignore?: AnyObject}; interface Window { [k: string]: any; } + +// Temporary globals (exported from modules, used by non-module files) + +// When creating now module files, these should all be commented out +// to make sure they're not being used globally in modules. + +declare var Battle: typeof import('./battle').Battle; +type Battle = import('./battle').Battle; +declare var BattleScene: typeof import('./battle-animations').BattleScene; +type BattleScene = import('./battle-animations').BattleScene; +declare var Pokemon: typeof import('./battle').Pokemon; +type Pokemon = import('./battle').Pokemon; +type ServerPokemon = import('./battle').ServerPokemon; +declare var BattleLog: typeof import('./battle-log').BattleLog; +type BattleLog = import('./battle-log').BattleLog; diff --git a/tslint.json b/tslint.json index 302df6498..280ac9e9a 100644 --- a/tslint.json +++ b/tslint.json @@ -36,6 +36,7 @@ "no-unnecessary-initializer": false, "object-literal-sort-keys": false, "object-literal-key-quotes": false, + "ordered-imports": false, "trailing-comma": [ true, {