Matou-Garou / patches /src /components /PixiStaticMap.tsx
Jofthomas's picture
Jofthomas HF staff
fix imports
14eb533
raw
history blame
5.53 kB
import { PixiComponent, applyDefaultProps } from '@pixi/react';
import * as PIXI from 'pixi.js';
import { AnimatedSprite, WorldMap, TileLayer } from '../../convex/aiTown/worldMap';
import * as campfire from '../../data/animations/campfire.json';
import * as gentlesparkle from '../../data/animations/gentlesparkle.json';
import * as gentlewaterfall from '../../data/animations/gentlewaterfall.json';
import * as gentlesplash from '../../data/animations/gentlesplash.json';
import * as windmill from '../../data/animations/windmill.json';
import React from 'react';
export type MapProps = {
map: WorldMap;
[k: string]: any;
};
const animations = {
'campfire.json': { spritesheet: campfire, url: '/assets/spritesheets/campfire.png' },
'gentlesparkle.json': {
spritesheet: gentlesparkle,
url: '/assets/spritesheets/gentlesparkle32.png',
},
'gentlewaterfall.json': {
spritesheet: gentlewaterfall,
url: '/assets/spritesheets/gentlewaterfall32.png',
},
'windmill.json': { spritesheet: windmill, url: '/assets/spritesheets/windmill.png' },
'gentlesplash.json': { spritesheet: gentlesplash,
url: '/assets/spritesheets/gentlewaterfall32.png',},
};
const createTiles = (map: WorldMap) => {
const numxtiles = Math.floor(map.tileSetDimX / map.tileDim);
const numytiles = Math.floor(map.tileSetDimY / map.tileDim);
const bt = PIXI.BaseTexture.from(map.tileSetUrl, {
scaleMode: PIXI.SCALE_MODES.NEAREST,
});
const tiles = [];
for (let x = 0; x < numxtiles; x++) {
for (let y = 0; y < numytiles; y++) {
tiles[x + y * numxtiles] = new PIXI.Texture(
bt,
new PIXI.Rectangle(x * map.tileDim, y * map.tileDim, map.tileDim, map.tileDim),
);
}
}
return tiles;
};
const renderMap = (container: PIXI.Container, map:WorldMap, tiles: PIXI.Texture[]) => {
const screenxtiles = map.bgTiles[0].length;
const screenytiles = map.bgTiles[0][0].length;
const allLayers = [...map.bgTiles, ...map.objectTiles, ...map.decorTiles];
// blit bg & object layers of map onto canvas
for (let i = 0; i < screenxtiles * screenytiles; i++) {
const x = i % screenxtiles;
const y = Math.floor(i / screenxtiles);
const xPx = x * map.tileDim;
const yPx = y * map.tileDim;
// Add all layers of backgrounds.
for (const layer of allLayers) {
const tileIndex = layer[x][y];
// Some layers may not have tiles at this location.
if (tileIndex === -1) continue;
const ctile = new PIXI.Sprite(tiles[tileIndex]);
ctile.x = xPx;
ctile.y = yPx;
container.addChild(ctile);
}
}
};
const createAnimatedSprites = (container: PIXI.Container, map: WorldMap) => {
const spritesBySheet = new Map<string, AnimatedSprite[]>();
for (const sprite of map.animatedSprites) {
const sheet = sprite.sheet;
if (!spritesBySheet.has(sheet)) {
spritesBySheet.set(sheet, []);
}
spritesBySheet.get(sheet)!.push(sprite);
}
for (const [sheet, sprites] of spritesBySheet.entries()) {
const animation = (animations as any)[sheet];
if (!animation) {
console.error('Could not find animation', sheet);
continue;
}
const { spritesheet, url } = animation;
const texture = PIXI.BaseTexture.from(url, {
scaleMode: PIXI.SCALE_MODES.NEAREST,
});
const spriteSheet = new PIXI.Spritesheet(texture, spritesheet);
spriteSheet.parse().then(() => {
for (const sprite of sprites) {
const pixiAnimation = spriteSheet.animations[sprite.animation];
if (!pixiAnimation) {
console.error('Failed to load animation', sprite);
continue;
}
const pixiSprite = new PIXI.AnimatedSprite(pixiAnimation);
pixiSprite.animationSpeed = 0.1;
pixiSprite.autoUpdate = true;
pixiSprite.x = sprite.x;
pixiSprite.y = sprite.y;
pixiSprite.width = sprite.w;
pixiSprite.height = sprite.h;
container.addChild(pixiSprite);
pixiSprite.play();
}
});
}
};
export const PixiStaticMapComponent = PixiComponent('StaticMap', {
create: (props: { map: WorldMap; [k: string]: any }) => {
const container = new PIXI.Container();
const tiles = createTiles(props.map);
renderMap(container, props.map, tiles);
createAnimatedSprites(container, props.map);
container.x = 0;
container.y = 0;
// Set the hit area manually to ensure `pointerdown` events are delivered to this container.
const screenxtiles = props.map.bgTiles[0].length;
const screenytiles = props.map.bgTiles[0][0].length;
container.interactive = true;
container.hitArea = new PIXI.Rectangle(
0,
0,
screenxtiles * props.map.tileDim,
screenytiles * props.map.tileDim,
);
return container;
},
applyProps: (instance, oldProps: any, newProps: any) => {
if (oldProps.map !== newProps.map) {
instance.removeChildren();
const tiles = createTiles(newProps.map);
renderMap(instance, newProps.map, tiles);
createAnimatedSprites(instance, newProps.map);
}
applyDefaultProps(instance, oldProps, newProps);
},
});
const PixiStaticMap = React.memo(
(props: MapProps) => {
return <PixiStaticMapComponent {...props} />;
},
(prevProps, nextProps) => {
return (
prevProps.map.bgTiles === nextProps.map.bgTiles &&
prevProps.map.objectTiles === nextProps.map.objectTiles &&
prevProps.map.decorTiles === nextProps.map.decorTiles
);
}
);
export default PixiStaticMap;