diff --git a/src/business/room_decor/room_decor.service.ts b/src/business/room_decor/room_decor.service.ts index 4738f80..eeeaa7b 100644 --- a/src/business/room_decor/room_decor.service.ts +++ b/src/business/room_decor/room_decor.service.ts @@ -83,11 +83,18 @@ export class RoomDecorService { if (!definition.rotation_steps.includes(rotationDegrees)) { throw new BadRequestException('该摆件不支持此旋转角度'); } + const positionX = this.snapToGrid(placement.position_x ?? definition.default_position.x, definition.grid_size); + const positionY = this.snapToGrid(placement.position_y ?? definition.default_position.y, definition.grid_size); + if (placement.placed && !this.isWithinPlacementBounds(positionX, positionY, definition)) { + throw new BadRequestException(definition.placement_surface === 'wall' + ? '墙面挂饰只能摆放在墙面网格内' + : '地面家具只能摆放在地面网格内'); + } const row = await this.roomDecorPlacementsService.savePlacement(userId, { ...placement, decor_id: decorId, - position_x: this.snapToGrid(placement.position_x, definition.grid_size), - position_y: this.snapToGrid(placement.position_y, definition.grid_size), + position_x: positionX, + position_y: positionY, scale: placement.scale ?? definition.default_scale, rotation_degrees: rotationDegrees, z_index: placement.z_index ?? definition.default_z_index, @@ -145,6 +152,7 @@ export class RoomDecorService { z_index: row.z_index ?? definition?.default_z_index ?? 0, category: definition?.category ?? 'floor', placement_surface: definition?.placement_surface ?? 'floor', + placement_bounds: definition?.placement_bounds, grid_size: definition?.grid_size ?? 16, rotation_steps: definition?.rotation_steps ?? [0], stackable: definition?.stackable ?? false, @@ -207,11 +215,15 @@ export class RoomDecorService { return definition?.rotation_steps.includes(rotation) ? rotation : definition?.default_rotation_degrees ?? 0; } - private snapToGrid(value: number | undefined, gridSize: number): number | undefined { - if (value === undefined) return undefined; + private snapToGrid(value: number, gridSize: number): number { return Math.round(value / gridSize) * gridSize; } + private isWithinPlacementBounds(x: number, y: number, definition: RoomDecorDefinition): boolean { + const bounds = definition.placement_bounds; + return x >= bounds.min_x && x <= bounds.max_x && y >= bounds.min_y && y <= bounds.max_y; + } + private usesLegacyPlacement(row: UserRoomDecorRow) { const legacy = ROOM_DECOR_LEGACY_DEFAULTS[row.decor_id]; if (!legacy) { diff --git a/src/business/room_decor/room_decor_catalog.ts b/src/business/room_decor/room_decor_catalog.ts index 739311a..9932491 100644 --- a/src/business/room_decor/room_decor_catalog.ts +++ b/src/business/room_decor/room_decor_catalog.ts @@ -4,6 +4,7 @@ export interface RoomDecorDefinition { item_id: string; category: 'floor' | 'rug' | 'wall'; placement_surface: 'floor' | 'wall'; + placement_bounds: RoomDecorPlacementBounds; grid_size: number; rotation_steps: number[]; stackable: boolean; @@ -33,6 +34,13 @@ export interface RoomDecorDefinition { }; } +export interface RoomDecorPlacementBounds { + min_x: number; + max_x: number; + min_y: number; + max_y: number; +} + export interface RoomDecorLegacyDefault { scale: number; default_position: { @@ -48,6 +56,18 @@ export const ROOM_DECOR_FLOOR_RUG_DEFAULT_SCALE = 1.0; export const ROOM_DECOR_WALL_DECOR_DEFAULT_SCALE = 0.04; export const ROOM_DECOR_LEGACY_BED_MAX_SCALE = 0.35; export const ROOM_DECOR_LEGACY_WALL_DECOR_SCALES = [0.7, 0.18]; +export const ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS: RoomDecorPlacementBounds = { + min_x: -426, + max_x: 426, + min_y: -176, + max_y: 212, +}; +export const ROOM_DECOR_WALL_PLACEMENT_BOUNDS: RoomDecorPlacementBounds = { + min_x: -378, + max_x: 378, + min_y: -258, + max_y: -192, +}; export const ROOM_DECOR_LEGACY_DEFAULTS: Record = { whale_floor_rug: { @@ -99,6 +119,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '鲸浪地毯', category: 'rug', placement_surface: 'floor', + placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0], stackable: false, @@ -116,6 +137,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '鲸语记忆板', category: 'wall', placement_surface: 'wall', + placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0], stackable: false, @@ -132,6 +154,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '鲸尾暖灯', category: 'floor', placement_surface: 'floor', + placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0, 90, 180, 270], stackable: false, @@ -151,6 +174,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '船舱小床', category: 'floor', placement_surface: 'floor', + placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0, 90, 180, 270], stackable: false, @@ -170,6 +194,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '海浪低床', category: 'floor', placement_surface: 'floor', + placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0, 90, 180, 270], stackable: false, @@ -189,6 +214,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '鲸尾床头床', category: 'floor', placement_surface: 'floor', + placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0, 90, 180, 270], stackable: false, @@ -208,6 +234,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '程序员鲸书架', category: 'floor', placement_surface: 'floor', + placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0, 90, 180, 270], stackable: false, @@ -227,6 +254,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: 'BUG特性徽章', category: 'wall', placement_surface: 'wall', + placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0], stackable: false, @@ -244,6 +272,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '佛系学习徽章', category: 'wall', placement_surface: 'wall', + placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0], stackable: false, @@ -261,6 +290,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [ name: '已经在做徽章', category: 'wall', placement_surface: 'wall', + placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS, grid_size: 16, rotation_steps: [0], stackable: false,