fix: enforce decor placement surfaces

This commit is contained in:
ANG-Server
2026-07-22 14:20:18 +08:00
parent b6b32f6676
commit 3f14230e15
2 changed files with 46 additions and 4 deletions

View File

@@ -83,11 +83,18 @@ export class RoomDecorService {
if (!definition.rotation_steps.includes(rotationDegrees)) { if (!definition.rotation_steps.includes(rotationDegrees)) {
throw new BadRequestException('该摆件不支持此旋转角度'); 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, { const row = await this.roomDecorPlacementsService.savePlacement(userId, {
...placement, ...placement,
decor_id: decorId, decor_id: decorId,
position_x: this.snapToGrid(placement.position_x, definition.grid_size), position_x: positionX,
position_y: this.snapToGrid(placement.position_y, definition.grid_size), position_y: positionY,
scale: placement.scale ?? definition.default_scale, scale: placement.scale ?? definition.default_scale,
rotation_degrees: rotationDegrees, rotation_degrees: rotationDegrees,
z_index: placement.z_index ?? definition.default_z_index, 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, z_index: row.z_index ?? definition?.default_z_index ?? 0,
category: definition?.category ?? 'floor', category: definition?.category ?? 'floor',
placement_surface: definition?.placement_surface ?? 'floor', placement_surface: definition?.placement_surface ?? 'floor',
placement_bounds: definition?.placement_bounds,
grid_size: definition?.grid_size ?? 16, grid_size: definition?.grid_size ?? 16,
rotation_steps: definition?.rotation_steps ?? [0], rotation_steps: definition?.rotation_steps ?? [0],
stackable: definition?.stackable ?? false, stackable: definition?.stackable ?? false,
@@ -207,11 +215,15 @@ export class RoomDecorService {
return definition?.rotation_steps.includes(rotation) ? rotation : definition?.default_rotation_degrees ?? 0; return definition?.rotation_steps.includes(rotation) ? rotation : definition?.default_rotation_degrees ?? 0;
} }
private snapToGrid(value: number | undefined, gridSize: number): number | undefined { private snapToGrid(value: number, gridSize: number): number {
if (value === undefined) return undefined;
return Math.round(value / gridSize) * gridSize; 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) { private usesLegacyPlacement(row: UserRoomDecorRow) {
const legacy = ROOM_DECOR_LEGACY_DEFAULTS[row.decor_id]; const legacy = ROOM_DECOR_LEGACY_DEFAULTS[row.decor_id];
if (!legacy) { if (!legacy) {

View File

@@ -4,6 +4,7 @@ export interface RoomDecorDefinition {
item_id: string; item_id: string;
category: 'floor' | 'rug' | 'wall'; category: 'floor' | 'rug' | 'wall';
placement_surface: 'floor' | 'wall'; placement_surface: 'floor' | 'wall';
placement_bounds: RoomDecorPlacementBounds;
grid_size: number; grid_size: number;
rotation_steps: number[]; rotation_steps: number[];
stackable: boolean; 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 { export interface RoomDecorLegacyDefault {
scale: number; scale: number;
default_position: { 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_WALL_DECOR_DEFAULT_SCALE = 0.04;
export const ROOM_DECOR_LEGACY_BED_MAX_SCALE = 0.35; 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_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<string, RoomDecorLegacyDefault> = { export const ROOM_DECOR_LEGACY_DEFAULTS: Record<string, RoomDecorLegacyDefault> = {
whale_floor_rug: { whale_floor_rug: {
@@ -99,6 +119,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '鲸浪地毯', name: '鲸浪地毯',
category: 'rug', category: 'rug',
placement_surface: 'floor', placement_surface: 'floor',
placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0], rotation_steps: [0],
stackable: false, stackable: false,
@@ -116,6 +137,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '鲸语记忆板', name: '鲸语记忆板',
category: 'wall', category: 'wall',
placement_surface: 'wall', placement_surface: 'wall',
placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0], rotation_steps: [0],
stackable: false, stackable: false,
@@ -132,6 +154,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '鲸尾暖灯', name: '鲸尾暖灯',
category: 'floor', category: 'floor',
placement_surface: 'floor', placement_surface: 'floor',
placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0, 90, 180, 270], rotation_steps: [0, 90, 180, 270],
stackable: false, stackable: false,
@@ -151,6 +174,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '船舱小床', name: '船舱小床',
category: 'floor', category: 'floor',
placement_surface: 'floor', placement_surface: 'floor',
placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0, 90, 180, 270], rotation_steps: [0, 90, 180, 270],
stackable: false, stackable: false,
@@ -170,6 +194,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '海浪低床', name: '海浪低床',
category: 'floor', category: 'floor',
placement_surface: 'floor', placement_surface: 'floor',
placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0, 90, 180, 270], rotation_steps: [0, 90, 180, 270],
stackable: false, stackable: false,
@@ -189,6 +214,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '鲸尾床头床', name: '鲸尾床头床',
category: 'floor', category: 'floor',
placement_surface: 'floor', placement_surface: 'floor',
placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0, 90, 180, 270], rotation_steps: [0, 90, 180, 270],
stackable: false, stackable: false,
@@ -208,6 +234,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '程序员鲸书架', name: '程序员鲸书架',
category: 'floor', category: 'floor',
placement_surface: 'floor', placement_surface: 'floor',
placement_bounds: ROOM_DECOR_FLOOR_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0, 90, 180, 270], rotation_steps: [0, 90, 180, 270],
stackable: false, stackable: false,
@@ -227,6 +254,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: 'BUG特性徽章', name: 'BUG特性徽章',
category: 'wall', category: 'wall',
placement_surface: 'wall', placement_surface: 'wall',
placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0], rotation_steps: [0],
stackable: false, stackable: false,
@@ -244,6 +272,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '佛系学习徽章', name: '佛系学习徽章',
category: 'wall', category: 'wall',
placement_surface: 'wall', placement_surface: 'wall',
placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0], rotation_steps: [0],
stackable: false, stackable: false,
@@ -261,6 +290,7 @@ export const ROOM_DECOR_DEFINITIONS: RoomDecorDefinition[] = [
name: '已经在做徽章', name: '已经在做徽章',
category: 'wall', category: 'wall',
placement_surface: 'wall', placement_surface: 'wall',
placement_bounds: ROOM_DECOR_WALL_PLACEMENT_BOUNDS,
grid_size: 16, grid_size: 16,
rotation_steps: [0], rotation_steps: [0],
stackable: false, stackable: false,