fix: show furniture-occupied tiles in the floor plan editor

The editor never requested occupied tiles, so tiles holding furniture
were indistinguishable from empty floor and could be edited/voided.

- request GetOccupiedTilesMessageComposer when the editor opens
- handle RoomOccupiedTilesMessageEvent -> SET_OCCUPIED_TILES
- new Tile.occupied flag (kept separate from `blocked`/void): occupied
  tiles render with a distinct marker and are protected from PAINT/
  ERASE/ADJUST and brush-to-selection edits
- occupied is purely informational and never changes the saved tilemap
  (no accidental voiding of floor under furni)

Tests: reducer cases for SET_OCCUPIED_TILES + edit protection; container
test asserts the occupied event is non-destructive on save; route the
canvas pointer test through elementFromPoint (jsdom has no getScreenCTM).
This commit is contained in:
medievalshell
2026-05-28 09:20:20 +02:00
parent 7a65e5bf6d
commit 48ed3ad7ba
7 changed files with 87 additions and 7 deletions
@@ -1,4 +1,4 @@
import { AddLinkEventTracker, convertNumbersForSaving, convertSettingToNumber, FloorHeightMapEvent, GetRoomEntryTileMessageComposer, ILinkEventTracker, RemoveLinkEventTracker, RoomEngineEvent, RoomEntryTileMessageEvent, RoomVisualizationSettingsEvent, UpdateFloorPropertiesMessageComposer } from '@nitrots/nitro-renderer';
import { AddLinkEventTracker, convertNumbersForSaving, convertSettingToNumber, FloorHeightMapEvent, GetOccupiedTilesMessageComposer, GetRoomEntryTileMessageComposer, ILinkEventTracker, RemoveLinkEventTracker, RoomEngineEvent, RoomEntryTileMessageEvent, RoomOccupiedTilesMessageEvent, RoomVisualizationSettingsEvent, UpdateFloorPropertiesMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useRef, useState } from 'react';
import { FaBolt, FaBoxOpen, FaCaretLeft, FaCaretRight } from 'react-icons/fa';
import { LocalizeText, SendMessageComposer } from '../../api';
@@ -50,8 +50,16 @@ export const FloorplanEditorView: FC = () =>
{
if(!isVisible) return;
SendMessageComposer(new GetRoomEntryTileMessageComposer());
// Ask the server which tiles currently hold furniture so they can be
// shown (and protected from editing) in the grid.
SendMessageComposer(new GetOccupiedTilesMessageComposer());
}, [ isVisible ]);
useMessageEvent<RoomOccupiedTilesMessageEvent>(RoomOccupiedTilesMessageEvent, event =>
{
dispatch({ type: 'SET_OCCUPIED_TILES', map: event.getParser().blockedTilesMap });
});
useMessageEvent<RoomEntryTileMessageEvent>(RoomEntryTileMessageEvent, event =>
{
const parser = event.getParser();