flow: room editing
This commit is contained in:
parent
65d5430ab0
commit
06b1a6ec66
|
@ -41,7 +41,7 @@ const RoomComponent = ({
|
||||||
const {uri, uuid} = useContext(Context);
|
const {uri, uuid} = useContext(Context);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const flecks = useFlecks();
|
const flecks = useFlecks();
|
||||||
const {Room} = flecks.get('$avocado/resource.resources');
|
const {Room, Tiles} = flecks.get('$avocado/resource.resources');
|
||||||
const previousResource = usePrevious(resource);
|
const previousResource = usePrevious(resource);
|
||||||
const [events, setEvents] = useState();
|
const [events, setEvents] = useState();
|
||||||
const [side, setSide] = useState(0);
|
const [side, setSide] = useState(0);
|
||||||
|
@ -53,31 +53,8 @@ const RoomComponent = ({
|
||||||
setEvents(stage?.events());
|
setEvents(stage?.events());
|
||||||
}, [setEvents]);
|
}, [setEvents]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (room) {
|
// First time, render the entire room.
|
||||||
const load = async () => {
|
if (!room) {
|
||||||
await Promise.all(
|
|
||||||
room.tiles.map(async (tiles, i) => {
|
|
||||||
await tiles.load(resource.tiles[i]);
|
|
||||||
const {area, tileSize} = tiles;
|
|
||||||
const viewport = Vector.mul(area, tileSize);
|
|
||||||
tiles.emit('update', Rectangle.compose([0, 0], viewport));
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if (resource.entities.length !== previousResource.entities.length) {
|
|
||||||
await room.entityList.load(resource.entities);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await Promise.all(
|
|
||||||
Object.values(room.entities)
|
|
||||||
.map(async (entity, i) => {
|
|
||||||
await entity.load(resource.entities[i]);
|
|
||||||
entity.renderTick(Infinity);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const loadRoom = async () => {
|
const loadRoom = async () => {
|
||||||
const room = await Room.load({
|
const room = await Room.load({
|
||||||
...resource,
|
...resource,
|
||||||
|
@ -90,6 +67,50 @@ const RoomComponent = ({
|
||||||
};
|
};
|
||||||
loadRoom();
|
loadRoom();
|
||||||
}
|
}
|
||||||
|
// Otherwise, we'll update the room.
|
||||||
|
else {
|
||||||
|
const updateRoom = async () => {
|
||||||
|
if (previousResource.entities !== resource.entities) {
|
||||||
|
if (previousResource.entities.length !== resource.entities.length) {
|
||||||
|
await room.entityList.load(resource.entities);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < resource.entities.length; i++) {
|
||||||
|
const element = resource.entities[i];
|
||||||
|
if (element !== previousResource.entities[i]) {
|
||||||
|
const entity = room.entityList.$$flatEntities[i];
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await entity.load(element);
|
||||||
|
entity.renderTick(Infinity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (previousResource.tiles !== resource.tiles) {
|
||||||
|
if (previousResource.tiles.length !== resource.tiles.length) {
|
||||||
|
await room.loadTiles(resource.tiles);
|
||||||
|
roomRenderable.renderChunksForExtent(Rectangle.compose([0, 0], room.size));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < resource.tiles.length; i++) {
|
||||||
|
const element = resource.tiles[i];
|
||||||
|
if (element !== previousResource.tiles[i]) {
|
||||||
|
const currentData = Tiles.inflate(element.data);
|
||||||
|
const previousData = Tiles.inflate(previousResource.tiles[i].data);
|
||||||
|
const updates = [];
|
||||||
|
for (let j = 0; j < currentData.length; j++) {
|
||||||
|
if (currentData[j] !== previousData[j]) {
|
||||||
|
updates.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
room.tiles[i].emit('update', Tiles.whereFromUpdates(updates));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateRoom();
|
||||||
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [resource, uri]);
|
}, [resource, uri]);
|
||||||
if (!roomRenderable) {
|
if (!roomRenderable) {
|
||||||
|
|
|
@ -75,18 +75,24 @@ function EntitiesPage({
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const onValue = ({
|
const onValue = ({
|
||||||
clientX,
|
|
||||||
clientY,
|
|
||||||
deltaY,
|
deltaY,
|
||||||
target,
|
position,
|
||||||
type,
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
const updatePosition = () => {
|
const updatePosition = () => {
|
||||||
const {left, top} = target.getBoundingClientRect();
|
|
||||||
patch.diff(
|
patch.diff(
|
||||||
`/entities/${selectedEntityIndex}`,
|
`/entities/${selectedEntityIndex}`,
|
||||||
entity,
|
entity,
|
||||||
merge(entity, {traits: {Positioned: {state: {x: clientX - left, y: clientY - top}}}}),
|
merge(
|
||||||
|
entity,
|
||||||
|
{
|
||||||
|
traits: {
|
||||||
|
Positioned: {
|
||||||
|
state: {x: Math.floor(position[0]), y: Math.floor(position[1])},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
@ -149,23 +149,10 @@ function TilesPage({
|
||||||
room.tiles[currentLayer].$$data,
|
room.tiles[currentLayer].$$data,
|
||||||
Rectangle.compose(origin, room.tiles[currentLayer].area),
|
Rectangle.compose(origin, room.tiles[currentLayer].area),
|
||||||
);
|
);
|
||||||
const [min, max] = [[Infinity, Infinity], [-Infinity, -Infinity]];
|
|
||||||
for (let i = 0; i < updates.length; i++) {
|
for (let i = 0; i < updates.length; i++) {
|
||||||
const update = updates[i];
|
const update = updates[i];
|
||||||
const [x, y] = [update % width, Math.floor(update / width)];
|
const [x, y] = [update % width, Math.floor(update / width)];
|
||||||
const offset = Vector.sub([x, y], origin);
|
const offset = Vector.sub([x, y], origin);
|
||||||
if (x < min[0]) {
|
|
||||||
min[0] = x;
|
|
||||||
}
|
|
||||||
else if (x > max[0]) {
|
|
||||||
max[0] = x;
|
|
||||||
}
|
|
||||||
if (y < min[1]) {
|
|
||||||
min[1] = y;
|
|
||||||
}
|
|
||||||
else if (y > max[1]) {
|
|
||||||
max[1] = y;
|
|
||||||
}
|
|
||||||
let tileLocation;
|
let tileLocation;
|
||||||
switch (fillMode) {
|
switch (fillMode) {
|
||||||
case 0: {
|
case 0: {
|
||||||
|
@ -192,7 +179,7 @@ function TilesPage({
|
||||||
}
|
}
|
||||||
$$data[update] = (tileLocation[1] * (viewport[0] / tileSize[0])) + tileLocation[0];
|
$$data[update] = (tileLocation[1] * (viewport[0] / tileSize[0])) + tileLocation[0];
|
||||||
}
|
}
|
||||||
const where = Rectangle.compose(min, Vector.add([1, 1], Vector.sub(max, min)));
|
const where = room.tiles[currentLayer].constructor.whereFromUpdates(updates);
|
||||||
room.tiles[currentLayer].emit('update', where);
|
room.tiles[currentLayer].emit('update', where);
|
||||||
};
|
};
|
||||||
const onValue = (
|
const onValue = (
|
||||||
|
@ -203,7 +190,7 @@ function TilesPage({
|
||||||
) => {
|
) => {
|
||||||
const origin = Vector.floor(Vector.div(position, tileSize));
|
const origin = Vector.floor(Vector.div(position, tileSize));
|
||||||
const edit = () => {
|
const edit = () => {
|
||||||
if (0 === room.tiles.length) {
|
if (0 === resource.tiles.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {data: originalData} = room.tiles[currentLayer].toJSON();
|
const {data: originalData} = room.tiles[currentLayer].toJSON();
|
||||||
|
@ -311,7 +298,7 @@ function TilesPage({
|
||||||
<div className={styles.layers}>
|
<div className={styles.layers}>
|
||||||
<ul>
|
<ul>
|
||||||
{
|
{
|
||||||
room.tiles.map((tiles, i) => (
|
resource.tiles.map((tiles, i) => (
|
||||||
// eslint-disable-next-line react/no-array-index-key
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<button
|
<button
|
||||||
|
@ -329,9 +316,9 @@ function TilesPage({
|
||||||
<span>Area</span>
|
<span>Area</span>
|
||||||
<span>
|
<span>
|
||||||
[
|
[
|
||||||
{tiles.width}
|
{tiles.area[0]}
|
||||||
{' x '}
|
{' x '}
|
||||||
{tiles.height}
|
{tiles.area[1]}
|
||||||
]
|
]
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
@ -349,15 +336,15 @@ function TilesPage({
|
||||||
<span>Full size</span>
|
<span>Full size</span>
|
||||||
<span>
|
<span>
|
||||||
[
|
[
|
||||||
{tiles.tileSize[0] * tiles.width}
|
{tiles.tileSize[0] * tiles.area[0]}
|
||||||
{' x '}
|
{' x '}
|
||||||
{tiles.tileSize[1] * tiles.height}
|
{tiles.tileSize[1] * tiles.area[1]}
|
||||||
]
|
]
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Tileset URI</span>
|
<span>Tileset URI</span>
|
||||||
<span>{tiles.atlas.image.uri}</span>
|
<span>{tiles.tileImageUri}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.visibility}>
|
<div className={styles.visibility}>
|
||||||
|
@ -438,14 +425,14 @@ function TilesPage({
|
||||||
<span>Area</span>
|
<span>Area</span>
|
||||||
<VectorComponent
|
<VectorComponent
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
value={room.tiles[currentLayer].area}
|
value={resource.tiles[currentLayer].area}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className={styles.tileSize}>
|
<label className={styles.tileSize}>
|
||||||
<span>Tile size</span>
|
<span>Tile size</span>
|
||||||
<VectorComponent
|
<VectorComponent
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
value={room.tiles[currentLayer].tileSize}
|
value={resource.tiles[currentLayer].tileSize}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div className={styles.grower} />
|
<div className={styles.grower} />
|
||||||
|
|
|
@ -12,9 +12,12 @@ export default class RoomRenderable extends Container {
|
||||||
constructor(room, renderer) {
|
constructor(room, renderer) {
|
||||||
super();
|
super();
|
||||||
this.lastExtent = [0, 0, 0, 0];
|
this.lastExtent = [0, 0, 0, 0];
|
||||||
|
this.room = room;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.entityListView = new EntityListView(room.entityList);
|
this.entityListView = new EntityListView(room.entityList);
|
||||||
this.tilesRenderables = room.tiles.map((tiles) => new TilesRenderable(tiles, renderer));
|
this.tilesRenderables = room.tiles.map((tiles) => new TilesRenderable(tiles, renderer));
|
||||||
|
room.on('tilesAdded', this.onTilesAdded, this);
|
||||||
|
room.on('tilesRemoved', this.onTilesRemoved, this);
|
||||||
this.addChildren(this.tilesRenderables);
|
this.addChildren(this.tilesRenderables);
|
||||||
this.addChild(this.entityListView);
|
this.addChild(this.entityListView);
|
||||||
this.sort();
|
this.sort();
|
||||||
|
@ -25,11 +28,28 @@ export default class RoomRenderable extends Container {
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
super.destroy();
|
super.destroy();
|
||||||
|
this.room.off('tilesAdded', this.onTilesAdded);
|
||||||
|
this.room.off('tilesRemoved', this.onTilesRemoved);
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
renderables.delete(this);
|
renderables.delete(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTilesAdded(tiles) {
|
||||||
|
const renderable = new TilesRenderable(tiles, this.renderer);
|
||||||
|
this.tilesRenderables.push(renderable);
|
||||||
|
this.addChild(renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTilesRemoved(tiles) {
|
||||||
|
const index = this.tilesRenderables.findIndex((renderable) => renderable.tiles === tiles);
|
||||||
|
if (-1 === index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [renderable] = this.tilesRenderables.splice(index, 1);
|
||||||
|
this.removeChild(renderable);
|
||||||
|
}
|
||||||
|
|
||||||
renderChunksForExtent(extent) {
|
renderChunksForExtent(extent) {
|
||||||
this.tilesRenderables.forEach((tilesRenderable) => {
|
this.tilesRenderables.forEach((tilesRenderable) => {
|
||||||
tilesRenderable.renderChunksForExtent(extent);
|
tilesRenderable.renderChunksForExtent(extent);
|
||||||
|
|
|
@ -15,10 +15,10 @@ export default (flecks) => {
|
||||||
);
|
);
|
||||||
return class Room extends decorate(JsonResource) {
|
return class Room extends decorate(JsonResource) {
|
||||||
|
|
||||||
entityList;
|
|
||||||
|
|
||||||
static Renderable = RoomRenderable;
|
static Renderable = RoomRenderable;
|
||||||
|
|
||||||
|
entityList;
|
||||||
|
|
||||||
tiles = [];
|
tiles = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -38,13 +38,18 @@ export default (flecks) => {
|
||||||
return this.entityList.entities;
|
return this.entityList.entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addTiles(tiles) {
|
||||||
|
this.tiles.push(tiles);
|
||||||
|
this.startSynchronizing(tiles);
|
||||||
|
this.emit('tilesAdded', tiles);
|
||||||
|
}
|
||||||
|
|
||||||
findEntity(uuid) {
|
findEntity(uuid) {
|
||||||
return this.entityList.findEntity(uuid);
|
return this.entityList.findEntity(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(json = {}) {
|
async load(json = {}) {
|
||||||
await super.load(json);
|
await super.load(json);
|
||||||
const {Tiles} = flecks.get('$avocado/resource.resources');
|
|
||||||
const {
|
const {
|
||||||
entities,
|
entities,
|
||||||
size = [0, 0],
|
size = [0, 0],
|
||||||
|
@ -56,13 +61,19 @@ export default (flecks) => {
|
||||||
this.startSynchronizing(this.entityList);
|
this.startSynchronizing(this.entityList);
|
||||||
this.size = size;
|
this.size = size;
|
||||||
if (tiles) {
|
if (tiles) {
|
||||||
this.tiles = await Promise.all(tiles.map((tiles, i) => Tiles.load({s13nId: i, ...tiles})));
|
await this.loadTiles(tiles);
|
||||||
this.tiles.forEach((tiles) => {
|
|
||||||
this.startSynchronizing(tiles);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadTiles(tiles) {
|
||||||
|
const {Tiles} = flecks.get('$avocado/resource.resources');
|
||||||
|
this.removeAllTiles();
|
||||||
|
(await Promise.all(tiles.map((json, i) => Tiles.load({s13nId: i, ...json}))))
|
||||||
|
.forEach((tiles) => {
|
||||||
|
this.addTiles(tiles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onEntityAdded(entity) {
|
onEntityAdded(entity) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
entity.room = this;
|
entity.room = this;
|
||||||
|
@ -77,10 +88,26 @@ export default (flecks) => {
|
||||||
this.emit('entityRemoved', entity);
|
this.emit('entityRemoved', entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeAllTiles() {
|
||||||
|
for (let i = this.tiles.length - 1; i >= 0; --i) {
|
||||||
|
this.removeTiles(this.tiles[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeEntity(entity) {
|
removeEntity(entity) {
|
||||||
this.entityList.removeEntity(entity);
|
this.entityList.removeEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeTiles(tiles) {
|
||||||
|
const index = this.tiles.indexOf(tiles);
|
||||||
|
if (-1 === index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [removed] = this.tiles.splice(index, 1);
|
||||||
|
this.stopSynchronizing(removed);
|
||||||
|
this.emit('tilesRemoved', removed);
|
||||||
|
}
|
||||||
|
|
||||||
get s13nId() {
|
get s13nId() {
|
||||||
return this.$$s13nId;
|
return this.$$s13nId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,11 @@ export default (flecks) => {
|
||||||
return this.constructor.indexHulls(indices, this.$$data, this.area);
|
return this.constructor.indexHulls(indices, this.$$data, this.area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inflate(data) {
|
||||||
|
const {buffer, byteOffset, length} = inflate(Buffer.from(data, 'base64'));
|
||||||
|
return new Uint16Array(buffer, byteOffset, length / 2);
|
||||||
|
}
|
||||||
|
|
||||||
async load(json) {
|
async load(json) {
|
||||||
const {
|
const {
|
||||||
area,
|
area,
|
||||||
|
@ -137,8 +142,7 @@ export default (flecks) => {
|
||||||
this.area = area;
|
this.area = area;
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
const {buffer, byteOffset, length} = inflate(Buffer.from(data, 'base64'));
|
this.$$data = this.constructor.inflate(data);
|
||||||
this.$$data = new Uint16Array(buffer, byteOffset, length / 2);
|
|
||||||
}
|
}
|
||||||
else if (area) {
|
else if (area) {
|
||||||
this.$$data = new Uint16Array(Vector.area(area));
|
this.$$data = new Uint16Array(Vector.area(area));
|
||||||
|
@ -298,6 +302,27 @@ export default (flecks) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static whereFromUpdates(updates) {
|
||||||
|
const [min, max] = [[Infinity, Infinity], [-Infinity, -Infinity]];
|
||||||
|
for (let i = 0; i < updates.length; i++) {
|
||||||
|
const update = updates[i];
|
||||||
|
const [x, y] = [update % this.width, Math.floor(update / this.width)];
|
||||||
|
if (x < min[0]) {
|
||||||
|
min[0] = x;
|
||||||
|
}
|
||||||
|
else if (x > max[0]) {
|
||||||
|
max[0] = x;
|
||||||
|
}
|
||||||
|
if (y < min[1]) {
|
||||||
|
min[1] = y;
|
||||||
|
}
|
||||||
|
else if (y > max[1]) {
|
||||||
|
max[1] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Rectangle.compose(min, Vector.add([1, 1], Vector.sub(max, min)));
|
||||||
|
}
|
||||||
|
|
||||||
get zIndex() {
|
get zIndex() {
|
||||||
return this.$$zIndex;
|
return this.$$zIndex;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user