// 背包系统:管理拾取的物品,支持网格类型的背包系统 export class Inventory { constructor(width = 6, height = 4) { this.width = width; // 背包宽度(网格数) this.height = height; // 背包高度(网格数) this.grid = this.createGrid(); // 创建背包网格 this.items = new Map(); // 存储背包中的物品 this.totalWeight = 0; // 总重量 this.totalVolume = 0; // 总体积 } /** * 创建背包网格 * @returns {Array} 二维数组表示的网格 */ createGrid() { const grid = []; for (let y = 0; y < this.height; y++) { grid[y] = []; for (let x = 0; x < this.width; x++) { grid[y][x] = null; // null表示空格子 } } return grid; } /** * 检查物品是否可以放置在指定位置(以物品中心为基础点) * @param {Object} item - 要放置的物品 * @param {number} centerX - 中心X坐标 * @param {number} centerY - 中心Y坐标 * @returns {boolean} 是否可以放置 */ canPlaceItem(item, centerX, centerY) { const { gridSize } = item; // 将中心坐标转换为左上角坐标 const topLeftX = Math.floor(centerX - (gridSize.width - 1) / 2); const topLeftY = Math.floor(centerY - (gridSize.height - 1) / 2); // 检查物品尺寸是否在背包范围内 if (topLeftX < 0 || topLeftY < 0 || topLeftX + gridSize.width > this.width || topLeftY + gridSize.height > this.height) { return false; } // 检查物品占用的所有格子是否都是空的或属于物品自身 for (let dy = 0; dy < gridSize.height; dy++) { for (let dx = 0; dx < gridSize.width; dx++) { const gridX = topLeftX + dx; const gridY = topLeftY + dy; const cellItemId = this.grid[gridY][gridX]; // 如果格子不是空的,且不是物品自身,则不能放置 if (cellItemId !== null && cellItemId !== item.id) { return false; } } } return true; } /** * 放置物品到背包中(以物品中心为基础点) * @param {Object} item - 要放置的物品 * @param {number} centerX - 中心X坐标 * @param {number} centerY - 中心Y坐标 * @returns {boolean} 是否放置成功 */ placeItem(item, centerX, centerY) { if (!this.canPlaceItem(item, centerX, centerY)) { return false; } const { gridSize } = item; // 将中心坐标转换为左上角坐标 const topLeftX = Math.floor(centerX - (gridSize.width - 1) / 2); const topLeftY = Math.floor(centerY - (gridSize.height - 1) / 2); // 计算物品中心坐标(用于物品信息) const centerPos = { x: centerX, y: centerY }; // 记录物品在背包中的位置信息 - 同时设置position和positionInInventory确保一致性 item.position = centerPos; item.positionInInventory = centerPos; item.inventory = this; item.inInventory = true; // 更新网格和物品列表 for (let dy = 0; dy < gridSize.height; dy++) { for (let dx = 0; dx < gridSize.width; dx++) { this.grid[topLeftY + dy][topLeftX + dx] = item.id; } } this.items.set(item.id, item); this.totalWeight += item.weight; this.totalVolume += item.volume; return true; } /** * 查找物品在背包中的位置 * @param {string} itemId - 物品ID * @returns {Object|null} 物品位置信息 */ findItemPosition(itemId) { for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { if (this.grid[y][x] === itemId) { return { x, y }; } } } return null; } /** * 从背包中移除物品 * @param {string} itemId - 物品ID * @returns {Object|null} 移除的物品 */ removeItem(itemId) { const item = this.items.get(itemId); if (!item) { return null; } const { position, gridSize } = item; // 计算物品占用的左上角坐标(从中心坐标转换) const topLeftX = Math.floor(position.x - (gridSize.width - 1) / 2); const topLeftY = Math.floor(position.y - (gridSize.height - 1) / 2); // 清空网格 for (let dy = 0; dy < gridSize.height; dy++) { for (let dx = 0; dx < gridSize.width; dx++) { this.grid[topLeftY + dy][topLeftX + dx] = null; } } // 更新物品信息和统计数据 - 同时清空position和positionInInventory item.position = null; item.positionInInventory = null; item.inventory = null; item.inInventory = false; this.items.delete(itemId); this.totalWeight -= item.weight; this.totalVolume -= item.volume; return item; } /** * 移动背包中的物品(以物品中心为基础点) * @param {string} itemId - 物品ID * @param {number} newCenterX - 新的中心X坐标 * @param {number} newCenterY - 新的中心Y坐标 * @returns {boolean} 是否移动成功 */ moveItem(itemId, newCenterX, newCenterY) { console.log('moveItem called:', itemId, 'to center position:', { newCenterX, newCenterY }); const item = this.items.get(itemId); if (!item) { console.log('moveItem failed: 物品不存在'); return false; } console.log('移动物品:', item.name, '当前中心位置:', item.position, '目标中心位置:', { newCenterX, newCenterY }); // 检查新位置是否可以放置 if (!this.canPlaceItem(item, newCenterX, newCenterY)) { console.log('moveItem failed: 新位置无法放置物品'); return false; } const { gridSize } = item; // 清空旧位置的网格(需要先计算旧位置的左上角坐标) const oldCenterX = item.position.x; const oldCenterY = item.position.y; const oldTopLeftX = Math.floor(oldCenterX - (gridSize.width - 1) / 2); const oldTopLeftY = Math.floor(oldCenterY - (gridSize.height - 1) / 2); console.log('清空旧位置网格:', { oldTopLeftX, oldTopLeftY }, '尺寸:', gridSize); for (let dy = 0; dy < gridSize.height; dy++) { for (let dx = 0; dx < gridSize.width; dx++) { this.grid[oldTopLeftY + dy][oldTopLeftX + dx] = null; } } // 更新物品位置为新的中心坐标 const newCenterPos = { x: newCenterX, y: newCenterY }; item.position = newCenterPos; item.positionInInventory = newCenterPos; console.log('更新物品中心位置:', item.position); // 更新新位置的网格(需要计算新位置的左上角坐标) const newTopLeftX = Math.floor(newCenterX - (gridSize.width - 1) / 2); const newTopLeftY = Math.floor(newCenterY - (gridSize.height - 1) / 2); for (let dy = 0; dy < gridSize.height; dy++) { for (let dx = 0; dx < gridSize.width; dx++) { this.grid[newTopLeftY + dy][newTopLeftX + dx] = item.id; } } console.log('moveItem success:', item.name, '已移动到中心位置', { newCenterX, newCenterY }); return true; } /** * 交换两个物品的位置(以物品中心为基础点) * @param {string} itemId1 - 第一个物品ID * @param {string} itemId2 - 第二个物品ID * @returns {boolean} 是否交换成功 */ swapItems(itemId1, itemId2) { console.log('swapItems called:', itemId1, '与', itemId2); const item1 = this.items.get(itemId1); const item2 = this.items.get(itemId2); if (!item1 || !item2) { console.log('swapItems failed: 物品不存在'); return false; } console.log('交换物品:', item1.name, '与', item2.name); console.log('物品1中心位置:', item1.position, '物品2中心位置:', item2.position); // 保存两个物品的中心位置 const centerPos1 = { ...item1.position }; const centerPos2 = { ...item2.position }; // 移除两个物品 console.log('移除两个物品'); this.removeItem(itemId1); this.removeItem(itemId2); // 尝试在对方位置放置(使用中心坐标) console.log('尝试将物品1放置到物品2的中心位置:', centerPos2); if (!this.placeItem(item1, centerPos2.x, centerPos2.y)) { // 回滚:将两个物品放回原位置 console.log('swapItems failed: 物品1无法放置到物品2的位置,回滚'); this.placeItem(item1, centerPos1.x, centerPos1.y); this.placeItem(item2, centerPos2.x, centerPos2.y); return false; } console.log('尝试将物品2放置到物品1的中心位置:', centerPos1); if (!this.placeItem(item2, centerPos1.x, centerPos1.y)) { // 回滚:将第一个物品放回原位置,第二个物品也放回原位置 console.log('swapItems failed: 物品2无法放置到物品1的位置,回滚'); this.removeItem(itemId1); this.placeItem(item1, centerPos1.x, centerPos1.y); this.placeItem(item2, centerPos2.x, centerPos2.y); return false; } console.log('swapItems success: 物品交换成功'); return true; } /** * 查找第一个可用的空位(返回中心坐标) * @param {Object} item - 要放置的物品 * @returns {Object|null} 可用中心位置 */ findEmptySpot(item) { const { gridSize } = item; // 确保物品尺寸不会超过背包尺寸 if (gridSize.width > this.width || gridSize.height > this.height) { console.log(`物品尺寸(${gridSize.width}x${gridSize.height})超过背包尺寸(${this.width}x${this.height})`); return null; } // 遍历背包网格,寻找可用位置(以左上角为起点) for (let y = 0; y <= this.height - gridSize.height; y++) { for (let x = 0; x <= this.width - gridSize.width; x++) { // 计算该左上角位置对应的中心坐标 // 注意:这里需要确保与canPlaceItem方法中的坐标转换对称 const centerX = x + (gridSize.width - 1) / 2; const centerY = y + (gridSize.height - 1) / 2; if (this.canPlaceItem(item, centerX, centerY)) { return { x: centerX, y: centerY }; } } } console.log("未找到可用空位"); return null; } /** * 添加物品到背包(自动寻找空位) * @param {Object} item - 要添加的物品 * @returns {boolean} 是否添加成功 */ addItem(item) { const spot = this.findEmptySpot(item); if (!spot) { return false; } return this.placeItem(item, spot.x, spot.y); } /** * 获取背包中指定位置的物品 * @param {number} x - X坐标 * @param {number} y - Y坐标 * @returns {Object|null} 物品 */ getItemAt(x, y) { if (x < 0 || y < 0 || x >= this.width || y >= this.height) { return null; } const itemId = this.grid[y][x]; return itemId ? this.items.get(itemId) : null; } /** * 获取背包中的所有物品 * @returns {Map} 物品映射 */ getAllItems() { return this.items; } /** * 获取背包的统计信息 * @returns {Object} 统计信息 */ getStats() { return { width: this.width, height: this.height, totalItems: this.items.size, totalWeight: this.totalWeight, totalVolume: this.totalVolume, emptySlots: this.calculateEmptySlots() }; } /** * 计算空位数 * @returns {number} 空位数 */ calculateEmptySlots() { let count = 0; for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { if (this.grid[y][x] === null) { count++; } } } return count; } /** * 清空背包 */ clear() { this.grid = this.createGrid(); this.items.clear(); this.totalWeight = 0; this.totalVolume = 0; } /** * 导出背包数据 * @returns {Object} 背包数据 */ exportData() { const items = []; this.items.forEach(item => { items.push({ ...item.getInfo(), position: item.position }); }); return { width: this.width, height: this.height, items: items }; } /** * 导入背包数据 * @param {Object} data - 背包数据 */ importData(data) { this.clear(); this.width = data.width; this.height = data.height; this.grid = this.createGrid(); data.items.forEach(itemData => { // 这里需要重新创建物品实例 // 假设Item类有一个静态方法createFromData const item = Item.createFromData(itemData); if (item) { this.placeItem(item, itemData.position.x, itemData.position.y); } }); } }