diablo2-web/src/game/drlg/drlg-anim.ts

212 lines
6.6 KiB
TypeScript

/**
* D2Common DRLG animated tiles: 1:1 port of D2MOO source/D2Common/src/Drlg/DrlgDrlgAnim.cpp
* (commit 5596f5c, MIT License, Copyright (c) 2020-2025 The Phrozen Keep community).
*/
import {
D2CMP_10079_GetTileFlags,
D2CMP_10081_GetTileRarity,
D2CMP_10088_GetTiles,
type D2TileLibraryEntryStrc,
} from './drlg-d2cmp.ts'
import { DRLGGRID_GetGridEntry } from './drlg-grid.ts'
import {
DRLGROOMTILE_InitFloorTileData,
DRLGROOMTILE_InitShadowTileData,
DRLGROOMTILE_InitWallTileData,
GetMapTileLayer,
MAPTILE_HIDDEN,
PackedTile,
TILETYPE_FLOOR,
TILETYPE_SHADOW,
tileGridOf,
} from './drlg-roomtile.ts'
import {
DRLGROOMFLAG_ANIMATED_FLOOR,
type D2DrlgAnimTileGridStrc,
type D2DrlgGridStrc,
type D2DrlgRoomStrc,
type D2DrlgTileDataStrc,
} from './drlg-types.ts'
// D2TileMaterialFlags (D2CMP.h)
const TILE_FLAGS_LAVA = 0x100
//D2Common.0x6FD75560
export function DRLGANIM_TestLoadAnimatedRoomTiles(
pDrlgRoom: D2DrlgRoomStrc,
pDrlgGrid: D2DrlgGridStrc,
pTileTypeGrid: D2DrlgGridStrc | null,
nTileType: number,
bKillEdgeX: boolean,
bKillEdgeY: boolean,
): void {
const ppTileLibraryEntry: D2TileLibraryEntryStrc[] = []
const pTiles = tileGridOf(pDrlgRoom).pTiles
const nMaxY = (pDrlgRoom.pDrlgCoord.nHeight + (bKillEdgeY ? 0 : 1)) | 0
const nMaxX = (pDrlgRoom.pDrlgCoord.nWidth + (bKillEdgeX ? 0 : 1)) | 0
for (let nY = 0; nY < nMaxY; nY += 1) {
for (let nX = 0; nX < nMaxX; nX += 1) {
const nGridEntry = DRLGGRID_GetGridEntry(pDrlgGrid, nX, nY) >>> 0
if (PackedTile.bShadow(nGridEntry) || PackedTile.bIsWall(nGridEntry) || PackedTile.bIsFloor(nGridEntry)) {
if (pTileTypeGrid) {
nTileType = DRLGGRID_GetGridEntry(pTileTypeGrid, nX, nY)
}
const nStyle = PackedTile.nTileStyle(nGridEntry)
const nSequence = PackedTile.nTileSequence(nGridEntry)
const nTilesCount = D2CMP_10088_GetTiles(pDrlgRoom.pTiles, nTileType, nStyle, nSequence, ppTileLibraryEntry, 40)
if (nTilesCount && D2CMP_10079_GetTileFlags(ppTileLibraryEntry[0]!) & TILE_FLAGS_LAVA) {
if (PackedTile.bIsFloor(nGridEntry)) {
pTiles.nFloors = (pTiles.nFloors + nTilesCount - 1) | 0
} else if (PackedTile.bIsWall(nGridEntry)) {
pTiles.nWalls = (pTiles.nWalls + nTilesCount - 1) | 0
} else {
pTiles.nRoofs = (pTiles.nRoofs + nTilesCount - 1) | 0
}
pDrlgRoom.dwFlags = (pDrlgRoom.dwFlags | DRLGROOMFLAG_ANIMATED_FLOOR) >>> 0
}
}
}
}
}
// Helper function
function DRLGANIM_FindAnimatedTileFrame(
pTileLibraryEntries: D2TileLibraryEntryStrc[],
nbTileEntries: number,
nRarity: number,
nStyle: number,
nSeq: number,
): D2TileLibraryEntryStrc {
for (let nEntryIndex = 0; nEntryIndex < nbTileEntries; nEntryIndex += 1) {
if (D2CMP_10081_GetTileRarity(pTileLibraryEntries[nEntryIndex]!) === nRarity) {
return pTileLibraryEntries[nEntryIndex]!
}
}
throw new Error(`Animating tiles missing Rarity ${nRarity} Tile - Style ${nStyle} Seq ${nSeq}`)
}
//D2Common.0x6FD75740
export function DRLGANIM_AllocAnimationTileGrids(
pDrlgRoom: D2DrlgRoomStrc,
nAnimationSpeed: number,
pWallGrid: D2DrlgGridStrc[],
nWalls: number,
pFloorGrid: D2DrlgGridStrc[],
nFloors: number,
pShadowGrid: D2DrlgGridStrc,
): void {
const pTileGrid = tileGridOf(pDrlgRoom)
DRLGANIM_AllocAnimationTileGrid(
pDrlgRoom,
nAnimationSpeed,
pTileGrid.pTiles.pWallTiles,
pTileGrid.nWalls,
pWallGrid,
nWalls,
)
DRLGANIM_AllocAnimationTileGrid(
pDrlgRoom,
nAnimationSpeed,
pTileGrid.pTiles.pFloorTiles,
pTileGrid.nFloors,
pFloorGrid,
nFloors,
)
DRLGANIM_AllocAnimationTileGrid(
pDrlgRoom,
nAnimationSpeed,
pTileGrid.pTiles.pRoofTiles,
pTileGrid.nShadows,
[pShadowGrid],
1,
)
}
//D2Common.0x6FD757B0
export function DRLGANIM_AllocAnimationTileGrid(
pDrlgRoom: D2DrlgRoomStrc,
nAnimationSpeed: number,
pTiles: D2DrlgTileDataStrc[] | null,
nTiles: number,
pDrlgGrid: D2DrlgGridStrc[],
_nUnused: number,
): void {
if (!pTiles) {
return
}
if (nAnimationSpeed === 0) {
nAnimationSpeed = 80
}
const pTileGrid = tileGridOf(pDrlgRoom)
const pTileLibraryEntries: D2TileLibraryEntryStrc[] = []
for (let i = 0; i < nTiles; i += 1) {
const pCurrentTileData = pTiles[i]!
if (pCurrentTileData.pTile && D2CMP_10079_GetTileFlags(pCurrentTileData.pTile) & TILE_FLAGS_LAVA) {
const nGridIdx = GetMapTileLayer(pCurrentTileData.dwFlags)
const pLayerGrid = pDrlgGrid[nGridIdx < 0 ? 0 : nGridIdx]!
const nGridEntry = DRLGGRID_GetGridEntry(pLayerGrid, pCurrentTileData.nPosX, pCurrentTileData.nPosY) >>> 0
const nStyle = PackedTile.nTileStyle(nGridEntry)
const nSeq = PackedTile.nTileSequence(nGridEntry)
const nFrames = D2CMP_10088_GetTiles(
pDrlgRoom.pTiles,
pCurrentTileData.nTileType,
nStyle,
nSeq,
pTileLibraryEntries,
40,
)
const ppMapTileData = new Array<D2DrlgTileDataStrc | null>(nFrames).fill(null)
const pDrlgAnimTileGrid: D2DrlgAnimTileGridStrc = {
ppMapTileData,
nFrames,
nCurrentFrame: 0,
nAnimationSpeed,
pNext: pTileGrid.pAnimTiles,
}
pTileGrid.pAnimTiles = pDrlgAnimTileGrid
// Animated tiles use rarity as frame index
pCurrentTileData.pTile = DRLGANIM_FindAnimatedTileFrame(pTileLibraryEntries, nFrames, 0, nStyle, nSeq)
pDrlgAnimTileGrid.ppMapTileData[0] = pCurrentTileData
const nX = (pCurrentTileData.nPosX + pDrlgRoom.pDrlgCoord.nPosX) | 0
const nY = (pCurrentTileData.nPosY + pDrlgRoom.pDrlgCoord.nPosY) | 0
for (let nRarity = 1; nRarity < nFrames; nRarity += 1) {
const pTileEntry = DRLGANIM_FindAnimatedTileFrame(pTileLibraryEntries, nFrames, nRarity, nStyle, nSeq)
let pAddedTile: D2DrlgTileDataStrc
if (pCurrentTileData.nTileType === TILETYPE_FLOOR) {
pAddedTile = DRLGROOMTILE_InitFloorTileData(pDrlgRoom, null, nX, nY, nGridEntry, pTileEntry)
} else if (pCurrentTileData.nTileType === TILETYPE_SHADOW) {
pAddedTile = DRLGROOMTILE_InitShadowTileData(pDrlgRoom, null, nX, nY, nGridEntry, pTileEntry)
} else {
pAddedTile = DRLGROOMTILE_InitWallTileData(
pDrlgRoom,
null,
nX,
nY,
nGridEntry,
pTileEntry,
pCurrentTileData.nTileType,
)
}
pDrlgAnimTileGrid.ppMapTileData[nRarity] = pAddedTile
// We start by displaying the first frame, hide all other frames tiles.
pAddedTile.dwFlags = (pAddedTile.dwFlags | MAPTILE_HIDDEN) >>> 0
}
}
}
}