feat: upgrade prologue exploration presentation
This commit is contained in:
296
src/game/data/explorationCharacterAssets.ts
Normal file
296
src/game/data/explorationCharacterAssets.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import type Phaser from 'phaser';
|
||||
import { releaseTextureSource } from '../render/loaderLifecycle';
|
||||
|
||||
export type ExplorationCharacterDirection = 'south' | 'east' | 'north' | 'west';
|
||||
export type ExplorationCharacterMotion = 'idle' | 'walk';
|
||||
|
||||
export const explorationCharacterFrameSize = 192;
|
||||
export const explorationCharacterIdleFrameCount = 8;
|
||||
export const explorationCharacterWalkFrameCount = 8;
|
||||
export const explorationCharacterFramesPerDirection =
|
||||
explorationCharacterIdleFrameCount + explorationCharacterWalkFrameCount;
|
||||
|
||||
export const explorationCharacterTextureKeyByUnitTextureKey = {
|
||||
'unit-liu-bei': 'exploration-liu-bei',
|
||||
'unit-guan-yu': 'exploration-guan-yu',
|
||||
'unit-zhang-fei': 'exploration-zhang-fei',
|
||||
'unit-shu-officer': 'exploration-shu-officer',
|
||||
'unit-shu-infantry': 'exploration-shu-infantry'
|
||||
} as const;
|
||||
|
||||
export const explorationCharacterNamedTextureKeyFallbacks = {
|
||||
'exploration-zhuo-recruiting-clerk': 'unit-shu-officer',
|
||||
'exploration-zhuo-villager': 'unit-shu-infantry',
|
||||
'exploration-zhuo-quartermaster': 'unit-shu-officer',
|
||||
'exploration-zou-jing': 'unit-shu-officer'
|
||||
} as const;
|
||||
|
||||
export type ExplorationCharacterUnitTextureKey =
|
||||
keyof typeof explorationCharacterTextureKeyByUnitTextureKey;
|
||||
export type ExplorationCharacterBaseTextureKey =
|
||||
(typeof explorationCharacterTextureKeyByUnitTextureKey)[ExplorationCharacterUnitTextureKey];
|
||||
export type ExplorationCharacterNamedTextureKey =
|
||||
keyof typeof explorationCharacterNamedTextureKeyFallbacks;
|
||||
export type ExplorationCharacterTextureKey =
|
||||
| ExplorationCharacterBaseTextureKey
|
||||
| ExplorationCharacterNamedTextureKey;
|
||||
|
||||
export type ExplorationCharacterAssetInfo = {
|
||||
unitTextureKey: ExplorationCharacterUnitTextureKey;
|
||||
key: ExplorationCharacterTextureKey;
|
||||
url: string;
|
||||
frameSize: number;
|
||||
frameWidth: number;
|
||||
frameHeight: number;
|
||||
format: 'webp';
|
||||
};
|
||||
|
||||
export const explorationCharacterUnitTextureKeys = Object.freeze(
|
||||
Object.keys(
|
||||
explorationCharacterTextureKeyByUnitTextureKey
|
||||
) as ExplorationCharacterUnitTextureKey[]
|
||||
);
|
||||
|
||||
export const explorationCharacterTextureKeys = Object.freeze(
|
||||
[
|
||||
...Object.values(explorationCharacterTextureKeyByUnitTextureKey),
|
||||
...Object.keys(explorationCharacterNamedTextureKeyFallbacks)
|
||||
] as ExplorationCharacterTextureKey[]
|
||||
);
|
||||
|
||||
const explorationCharacterDirections = [
|
||||
'south',
|
||||
'east',
|
||||
'north',
|
||||
'west'
|
||||
] as const satisfies readonly ExplorationCharacterDirection[];
|
||||
const explorationCharacterSheetRows: Record<ExplorationCharacterDirection, number> = {
|
||||
south: 0,
|
||||
east: 1,
|
||||
north: 2,
|
||||
west: 3
|
||||
};
|
||||
const explorationCharacterIdleFrameRate = 6;
|
||||
const explorationCharacterWalkFrameRate = 10;
|
||||
|
||||
const explorationCharacterSheetModules = import.meta.glob(
|
||||
'../../assets/images/exploration/characters/exploration-*.webp',
|
||||
{
|
||||
eager: true,
|
||||
import: 'default'
|
||||
}
|
||||
) as Record<string, string>;
|
||||
|
||||
function textureKeyFromPath(path: string) {
|
||||
const fileName = path.split('/').pop() ?? path;
|
||||
return fileName.replace(/\.webp$/i, '');
|
||||
}
|
||||
|
||||
const explorationCharacterSheetUrlsByKey = new Map(
|
||||
Object.entries(explorationCharacterSheetModules).map(([path, url]) => [
|
||||
textureKeyFromPath(path),
|
||||
url
|
||||
])
|
||||
);
|
||||
const explorationCharacterTextureKeySet = new Set<ExplorationCharacterTextureKey>(
|
||||
explorationCharacterTextureKeys
|
||||
);
|
||||
const unitTextureKeyByExplorationCharacterTextureKey = new Map<
|
||||
ExplorationCharacterBaseTextureKey,
|
||||
ExplorationCharacterUnitTextureKey
|
||||
>(
|
||||
Object.entries(explorationCharacterTextureKeyByUnitTextureKey).map(
|
||||
([unitTextureKey, textureKey]) => [
|
||||
textureKey,
|
||||
unitTextureKey as ExplorationCharacterUnitTextureKey
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
export function explorationCharacterTextureKeyForUnitTextureKey(
|
||||
unitTextureKey: string
|
||||
): ExplorationCharacterBaseTextureKey | undefined {
|
||||
if (!(unitTextureKey in explorationCharacterTextureKeyByUnitTextureKey)) {
|
||||
return undefined;
|
||||
}
|
||||
return explorationCharacterTextureKeyByUnitTextureKey[
|
||||
unitTextureKey as ExplorationCharacterUnitTextureKey
|
||||
];
|
||||
}
|
||||
|
||||
export function explorationCharacterAssetInfo(
|
||||
textureKeyOrUnitTextureKey: string
|
||||
): ExplorationCharacterAssetInfo | undefined {
|
||||
const key = explorationCharacterTextureKeyForSourceKey(textureKeyOrUnitTextureKey);
|
||||
const url = key ? explorationCharacterSheetUrlsByKey.get(key) : undefined;
|
||||
const unitTextureKey = key ? fallbackUnitTextureKeyForExplorationTextureKey(key) : undefined;
|
||||
if (!key || !url || !unitTextureKey) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
unitTextureKey,
|
||||
key,
|
||||
url,
|
||||
frameSize: explorationCharacterFrameSize,
|
||||
frameWidth: explorationCharacterFrameSize,
|
||||
frameHeight: explorationCharacterFrameSize,
|
||||
format: 'webp'
|
||||
};
|
||||
}
|
||||
|
||||
export function explorationCharacterAssetInfos(
|
||||
textureKeysOrUnitTextureKeys: Iterable<string> = explorationCharacterUnitTextureKeys
|
||||
) {
|
||||
const assets: ExplorationCharacterAssetInfo[] = [];
|
||||
const seenTextureKeys = new Set<ExplorationCharacterTextureKey>();
|
||||
Array.from(new Set(textureKeysOrUnitTextureKeys)).forEach((textureKeyOrUnitTextureKey) => {
|
||||
const asset = explorationCharacterAssetInfo(textureKeyOrUnitTextureKey);
|
||||
if (!asset || seenTextureKeys.has(asset.key)) {
|
||||
return;
|
||||
}
|
||||
seenTextureKeys.add(asset.key);
|
||||
assets.push(asset);
|
||||
});
|
||||
return assets;
|
||||
}
|
||||
|
||||
export function hasExplorationCharacterAsset(textureKeyOrUnitTextureKey: string) {
|
||||
return explorationCharacterAssetInfo(textureKeyOrUnitTextureKey) !== undefined;
|
||||
}
|
||||
|
||||
function explorationCharacterTextureKeyForSourceKey(
|
||||
textureKeyOrUnitTextureKey: string
|
||||
): ExplorationCharacterTextureKey | undefined {
|
||||
return explorationCharacterTextureKeyForUnitTextureKey(textureKeyOrUnitTextureKey) ??
|
||||
(
|
||||
explorationCharacterTextureKeySet.has(
|
||||
textureKeyOrUnitTextureKey as ExplorationCharacterTextureKey
|
||||
)
|
||||
? textureKeyOrUnitTextureKey as ExplorationCharacterTextureKey
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
function fallbackUnitTextureKeyForExplorationTextureKey(
|
||||
textureKey: ExplorationCharacterTextureKey
|
||||
): ExplorationCharacterUnitTextureKey | undefined {
|
||||
const baseUnitTextureKey = unitTextureKeyByExplorationCharacterTextureKey.get(
|
||||
textureKey as ExplorationCharacterBaseTextureKey
|
||||
);
|
||||
if (baseUnitTextureKey) {
|
||||
return baseUnitTextureKey;
|
||||
}
|
||||
if (textureKey in explorationCharacterNamedTextureKeyFallbacks) {
|
||||
return explorationCharacterNamedTextureKeyFallbacks[
|
||||
textureKey as ExplorationCharacterNamedTextureKey
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function explorationCharacterAnimationKey(
|
||||
textureKey: ExplorationCharacterTextureKey,
|
||||
motion: ExplorationCharacterMotion,
|
||||
direction: ExplorationCharacterDirection
|
||||
) {
|
||||
return `${textureKey}-${motion}-${direction}`;
|
||||
}
|
||||
|
||||
export function ensureExplorationCharacterAnimations(
|
||||
scene: Phaser.Scene,
|
||||
textureKeysOrUnitTextureKeys: Iterable<string>
|
||||
) {
|
||||
explorationCharacterAssetInfos(textureKeysOrUnitTextureKeys).forEach(({ key }) => {
|
||||
if (!scene.textures.exists(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
explorationCharacterDirections.forEach((direction) => {
|
||||
const rowStart =
|
||||
explorationCharacterSheetRows[direction] * explorationCharacterFramesPerDirection;
|
||||
const idleFrames = Array.from(
|
||||
{ length: explorationCharacterIdleFrameCount },
|
||||
(_, frameIndex) => ({
|
||||
key,
|
||||
frame: rowStart + frameIndex
|
||||
})
|
||||
);
|
||||
const walkFrames = Array.from(
|
||||
{ length: explorationCharacterWalkFrameCount },
|
||||
(_, frameIndex) => ({
|
||||
key,
|
||||
frame: rowStart + explorationCharacterIdleFrameCount + frameIndex
|
||||
})
|
||||
);
|
||||
const idleAnimationKey = explorationCharacterAnimationKey(key, 'idle', direction);
|
||||
const walkAnimationKey = explorationCharacterAnimationKey(key, 'walk', direction);
|
||||
|
||||
if (!scene.anims.exists(idleAnimationKey)) {
|
||||
scene.anims.create({
|
||||
key: idleAnimationKey,
|
||||
frames: idleFrames,
|
||||
frameRate: explorationCharacterIdleFrameRate,
|
||||
repeat: -1
|
||||
});
|
||||
}
|
||||
if (!scene.anims.exists(walkAnimationKey)) {
|
||||
scene.anims.create({
|
||||
key: walkAnimationKey,
|
||||
frames: walkFrames,
|
||||
frameRate: explorationCharacterWalkFrameRate,
|
||||
repeat: -1
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function releaseExplorationCharacterTextures(
|
||||
scene: Phaser.Scene,
|
||||
keepTextureKeysOrUnitTextureKeys: Iterable<string> = []
|
||||
) {
|
||||
const keepTextureKeys = new Set(
|
||||
Array.from(keepTextureKeysOrUnitTextureKeys)
|
||||
.map((textureKey) => explorationCharacterTextureKeyForSourceKey(textureKey))
|
||||
.filter((key): key is ExplorationCharacterTextureKey => key !== undefined)
|
||||
);
|
||||
return releaseExplorationCharacterTextureKeys(
|
||||
scene,
|
||||
explorationCharacterTextureKeys.filter((textureKey) => !keepTextureKeys.has(textureKey))
|
||||
);
|
||||
}
|
||||
|
||||
export function releaseExplorationCharacterTextureKeys(
|
||||
scene: Phaser.Scene,
|
||||
textureKeysOrUnitTextureKeys: Iterable<string>
|
||||
) {
|
||||
let releasedTextureCount = 0;
|
||||
const textureKeys = new Set(
|
||||
Array.from(textureKeysOrUnitTextureKeys)
|
||||
.map((textureKey) => explorationCharacterTextureKeyForSourceKey(textureKey))
|
||||
.filter((key): key is ExplorationCharacterTextureKey => key !== undefined)
|
||||
);
|
||||
|
||||
textureKeys.forEach((textureKey) => {
|
||||
releaseExplorationCharacterAnimations(scene, textureKey);
|
||||
if (releaseTextureSource(scene, textureKey)) {
|
||||
releasedTextureCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return releasedTextureCount;
|
||||
}
|
||||
|
||||
function releaseExplorationCharacterAnimations(
|
||||
scene: Phaser.Scene,
|
||||
textureKey: ExplorationCharacterTextureKey
|
||||
) {
|
||||
explorationCharacterDirections.forEach((direction) => {
|
||||
(['idle', 'walk'] as ExplorationCharacterMotion[]).forEach((motion) => {
|
||||
const animationKey = explorationCharacterAnimationKey(textureKey, motion, direction);
|
||||
if (scene.anims.exists(animationKey)) {
|
||||
scene.anims.remove(animationKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user