mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-29 22:31:29 +08:00
1264 lines
40 KiB
JavaScript
1264 lines
40 KiB
JavaScript
// 背包UI组件:负责显示背包界面和处理用户交互
|
||
|
||
export class InventoryUI {
|
||
constructor(scene, inventory, cellSize = 40) {
|
||
this.scene = scene;
|
||
this.inventory = inventory;
|
||
this.cellSize = cellSize;
|
||
this.isOpen = false;
|
||
this.selectedItem = null;
|
||
this.hoveredCell = null;
|
||
|
||
// 拖动相关属性
|
||
this.isDragging = false;
|
||
this.draggedItem = null;
|
||
this.draggedSprite = null;
|
||
this.dragOffset = { x: 0, y: 0 };
|
||
|
||
this.container = null;
|
||
this.gridCells = [];
|
||
this.itemSprites = new Map();
|
||
|
||
// 计算背包位置(相机右侧)
|
||
this.calculatePosition();
|
||
|
||
this.createUI();
|
||
|
||
// 添加全局鼠标移动事件监听
|
||
this.scene.input.on('pointermove', (pointer) => {
|
||
this.onMouseMove(pointer);
|
||
});
|
||
|
||
// 添加全局鼠标释放事件监听
|
||
this.scene.input.on('pointerup', (pointer) => {
|
||
this.onMouseUp(pointer);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 计算背包位置(屏幕左上角)
|
||
*/
|
||
calculatePosition() {
|
||
// 背包宽度
|
||
const inventoryWidth = this.inventory.width * this.cellSize + 20;
|
||
// 固定在屏幕左上角,有一定边距
|
||
this.x = 20;
|
||
this.y = 20;
|
||
}
|
||
|
||
/**
|
||
* 创建背包UI
|
||
*/
|
||
createUI() {
|
||
// 创建容器
|
||
this.container = this.scene.add.container(this.x, this.y);
|
||
this.container.setDepth(100);
|
||
this.container.setScrollFactor(0); // 固定在屏幕上,不随相机移动
|
||
|
||
// 创建背景
|
||
const background = this.scene.add.rectangle(
|
||
0, 0,
|
||
this.inventory.width * this.cellSize + 20,
|
||
this.inventory.height * this.cellSize + 20,
|
||
0x333333, 0.9
|
||
);
|
||
background.setOrigin(0, 0);
|
||
background.setScrollFactor(0); // 固定在屏幕上
|
||
background.setDepth(-1); // 确保背景在所有内容下方
|
||
|
||
// 添加交互,阻止点击事件穿透到下方地图
|
||
background.setInteractive();
|
||
background.on('pointerdown', (pointer) => {
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
background.on('pointerover', (pointer) => {
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
background.on('pointerout', (pointer) => {
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
this.container.add(background);
|
||
|
||
// 创建网格
|
||
this.createGrid();
|
||
|
||
// 创建关闭按钮
|
||
this.createCloseButton();
|
||
|
||
// 创建统计信息
|
||
this.createStats();
|
||
|
||
// 初始化时隐藏
|
||
this.hide();
|
||
}
|
||
|
||
/**
|
||
* 创建背包网格
|
||
*/
|
||
createGrid() {
|
||
const offsetX = 10;
|
||
const offsetY = 10;
|
||
|
||
for (let y = 0; y < this.inventory.height; y++) {
|
||
this.gridCells[y] = [];
|
||
for (let x = 0; x < this.inventory.width; x++) {
|
||
const cellX = offsetX + x * this.cellSize;
|
||
const cellY = offsetY + y * this.cellSize;
|
||
|
||
// 创建单元格
|
||
const cell = this.scene.add.rectangle(cellX, cellY, this.cellSize, this.cellSize, 0x666666, 0.5);
|
||
cell.setOrigin(0, 0);
|
||
cell.setStrokeStyle(1, 0xffffff);
|
||
cell.setScrollFactor(0); // 固定在屏幕上,不随相机移动
|
||
|
||
// 添加交互
|
||
cell.setInteractive();
|
||
|
||
// 绑定点击事件
|
||
cell.on('pointerdown', (pointer) => {
|
||
console.log('单元格点击事件触发:', { x, y });
|
||
this.onCellClicked(x, y);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
cell.on('pointerover', (pointer) => {
|
||
this.onCellHovered(x, y);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
cell.on('pointerout', (pointer) => {
|
||
this.onCellUnhovered();
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
this.container.add(cell);
|
||
this.gridCells[y][x] = cell;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建关闭按钮
|
||
*/
|
||
createCloseButton() {
|
||
const buttonX = this.inventory.width * this.cellSize + 25;
|
||
const buttonY = 10;
|
||
|
||
const button = this.scene.add.text(buttonX, buttonY, 'X', {
|
||
font: '24px Arial',
|
||
fill: '#ffffff'
|
||
});
|
||
button.setInteractive({
|
||
useHandCursor: true
|
||
});
|
||
|
||
button.on('pointerdown', (pointer) => {
|
||
this.hide();
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
button.on('pointerover', (pointer) => {
|
||
button.setFill('#ff0000');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
button.on('pointerout', (pointer) => {
|
||
button.setFill('#ffffff');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
this.container.add(button);
|
||
}
|
||
|
||
/**
|
||
* 创建统计信息
|
||
*/
|
||
createStats() {
|
||
const statsX = this.inventory.width * this.cellSize + 30;
|
||
const statsY = 50;
|
||
|
||
// 创建统计信息背景
|
||
const statsBackground = this.scene.add.rectangle(
|
||
statsX - 20,
|
||
statsY - 20,
|
||
120, 100,
|
||
0x222222, 0.9
|
||
);
|
||
statsBackground.setOrigin(0, 0);
|
||
statsBackground.setScrollFactor(0);
|
||
|
||
// 添加交互,阻止事件穿透
|
||
statsBackground.setInteractive();
|
||
statsBackground.on('pointerdown', (pointer) => {
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
this.container.add(statsBackground);
|
||
|
||
this.statsText = this.scene.add.text(statsX, statsY, '', {
|
||
font: '12px Arial',
|
||
fill: '#ffffff'
|
||
});
|
||
|
||
this.container.add(this.statsText);
|
||
this.updateStats();
|
||
}
|
||
|
||
/**
|
||
* 更新统计信息
|
||
*/
|
||
updateStats() {
|
||
const stats = this.inventory.getStats();
|
||
this.statsText.setText([
|
||
`物品数量: ${stats.totalItems}`,
|
||
`总重量: ${stats.totalWeight.toFixed(1)}`,
|
||
`总体积: ${stats.totalVolume.toFixed(1)}`,
|
||
`空位数: ${stats.emptySlots}`
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 物品精灵点击事件
|
||
* @param {Object} item - 点击的物品
|
||
*/
|
||
onItemSpriteClicked(item) {
|
||
console.log('onItemSpriteClicked:', item.name, 'ID:', item.id);
|
||
// 如果是右键点击,使用物品
|
||
if (this.scene.input.mousePointer.rightButtonDown()) {
|
||
console.log('右键点击物品,使用物品:', item.name);
|
||
this.useItem(item);
|
||
} else if (this.selectedItem && this.selectedItem.id === item.id) {
|
||
// 如果是左键点击已选中的物品,取消选择
|
||
console.log('左键点击已选中物品,取消选择:', item.name);
|
||
this.deselectItem();
|
||
} else {
|
||
// 如果是左键点击其他物品,选中当前物品
|
||
// 如果已经有选中的物品,先取消选择
|
||
if (this.selectedItem) {
|
||
console.log('已有选中物品,先取消选择:', this.selectedItem.name);
|
||
this.deselectItem();
|
||
}
|
||
// 选中当前物品
|
||
console.log('选中物品:', item.name);
|
||
this.selectItem(item);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用物品
|
||
* @param {Object} item - 要使用的物品
|
||
*/
|
||
useItem(item) {
|
||
console.log('useItem:', item.name, 'ID:', item.id);
|
||
|
||
// 获取当前选中的角色(假设场景中有player属性)
|
||
const user = this.scene.player;
|
||
|
||
if (user) {
|
||
console.log('使用物品的角色:', user.name);
|
||
|
||
// 确保物品有场景引用
|
||
item.scene = this.scene;
|
||
console.log('DEBUG: 设置物品场景引用:', item.scene ? '成功' : '失败');
|
||
|
||
// 调用物品的useFromInventory方法,从背包中使用物品
|
||
// 注意:物品的具体使用方法(如useSeed)中会处理从背包移除的逻辑
|
||
item.useFromInventory(user);
|
||
|
||
// 更新UI,因为物品可能已经被移除
|
||
this.updateUI();
|
||
|
||
// 对于种子类型的物品,延迟取消选择,避免背包立即关闭导致用户体验不佳
|
||
// 这样用户可以看到种植模式的变化
|
||
if (item.type !== 'seed') {
|
||
// 非种子类型物品,立即取消选择
|
||
this.deselectItem();
|
||
} else {
|
||
// 种子类型物品,延迟取消选择,给用户视觉反馈
|
||
console.log('种子物品使用后,延迟取消选择');
|
||
// 不立即取消选择,让用户看到种植模式的变化
|
||
// 种植模式会在后续操作中自动取消选择
|
||
}
|
||
} else {
|
||
console.log('没有选中的角色,无法使用物品');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 物品精灵鼠标按下事件,用于记录点击状态和位置
|
||
* @param {Object} item - 物品
|
||
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||
*/
|
||
onItemSpriteDown(item, pointer) {
|
||
console.log('onItemSpriteDown:', item.name, '记录点击位置');
|
||
|
||
// 右键点击直接使用物品,不处理选择和拖动
|
||
if (pointer.rightButtonDown()) {
|
||
console.log('右键点击物品精灵,使用物品:', item.name);
|
||
this.useItem(item);
|
||
return; // 直接返回,不处理后续逻辑
|
||
}
|
||
|
||
// 左键点击处理选择和拖动
|
||
// 记录点击的物品和位置,用于判断是否开始拖动
|
||
this.clickItem = item;
|
||
this.clickPosition = { x: pointer.x, y: pointer.y };
|
||
this.clickTime = Date.now();
|
||
|
||
// 切换选择状态:如果已选中,则取消选择;如果未选中,则选中
|
||
if (this.selectedItem && this.selectedItem.id === item.id) {
|
||
// 如果点击的是已选中物品,取消选择
|
||
console.log('左键点击已选中物品精灵,取消选择:', item.name);
|
||
this.deselectItem();
|
||
} else {
|
||
// 如果没有选中物品或点击的是另一个物品,选中当前物品
|
||
if (this.selectedItem) {
|
||
this.deselectItem();
|
||
}
|
||
this.selectItem(item);
|
||
}
|
||
|
||
// 不立即开始拖动,只在鼠标移动超过阈值时才开始
|
||
this.isDragging = false;
|
||
this.draggedItem = null;
|
||
this.draggedSprite = null;
|
||
}
|
||
|
||
/**
|
||
* 全局鼠标移动事件处理
|
||
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||
*/
|
||
onMouseMove(pointer) {
|
||
// 如果还没有开始拖动,检查是否应该开始拖动
|
||
if (this.clickItem && !this.isDragging) {
|
||
const distance = Math.sqrt(
|
||
Math.pow(pointer.x - this.clickPosition.x, 2) +
|
||
Math.pow(pointer.y - this.clickPosition.y, 2)
|
||
);
|
||
|
||
// 如果移动距离超过阈值,开始拖动
|
||
if (distance > 5) {
|
||
this.startDragging();
|
||
}
|
||
}
|
||
|
||
// 如果正在拖动,更新精灵位置,使其中心锁定在鼠标位置
|
||
if (this.isDragging && this.draggedSprite) {
|
||
// 更新拖动精灵的位置,使其中心锁定在鼠标位置
|
||
const spriteX = pointer.x - this.container.x - this.spriteCenterOffset.x;
|
||
const spriteY = pointer.y - this.container.y - this.spriteCenterOffset.y;
|
||
|
||
this.draggedSprite.setPosition(spriteX, spriteY);
|
||
|
||
// 检查鼠标是否悬停在某个单元格上
|
||
this.updateDragHover(pointer);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 开始拖动物品
|
||
*/
|
||
startDragging() {
|
||
console.log('startDragging:', this.clickItem.name);
|
||
this.isDragging = true;
|
||
this.draggedItem = this.clickItem;
|
||
|
||
// 拖动时隐藏物品详情
|
||
this.hideItemInfo();
|
||
|
||
// 获取物品精灵
|
||
const spriteObj = this.itemSprites.get(this.draggedItem.id);
|
||
if (spriteObj) {
|
||
this.draggedSprite = spriteObj.sprite;
|
||
// 获取精灵尺寸
|
||
const spriteRect = this.draggedSprite.getBounds();
|
||
// 计算精灵中心偏移(用于后续位置计算)
|
||
this.spriteCenterOffset = {
|
||
x: spriteRect.width / 2,
|
||
y: spriteRect.height / 2
|
||
};
|
||
|
||
// 提升精灵深度,确保拖动时显示在最上层
|
||
this.draggedSprite.setDepth(10);
|
||
|
||
// 创建预计放下位置的预览精灵
|
||
this.createDragPreview();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建预计放下位置的预览
|
||
*/
|
||
createDragPreview() {
|
||
if (this.dragPreview) {
|
||
this.dragPreview.destroy();
|
||
this.dragPreview = null;
|
||
}
|
||
|
||
const { width, height } = this.draggedItem.gridSize;
|
||
const previewWidth = width * this.cellSize;
|
||
const previewHeight = height * this.cellSize;
|
||
|
||
// 创建预览矩形(半透明,用于显示预计放下位置)
|
||
this.dragPreview = this.scene.add.rectangle(0, 0, previewWidth, previewHeight, 0xffffff, 0.3);
|
||
this.dragPreview.setOrigin(0, 0);
|
||
this.dragPreview.setStrokeStyle(2, 0x00ff00, 1);
|
||
this.dragPreview.setScrollFactor(0); // 固定在屏幕上
|
||
this.dragPreview.setDepth(5); // 确保预览在背景之上,在拖动的精灵之下
|
||
|
||
this.container.add(this.dragPreview);
|
||
}
|
||
|
||
/**
|
||
* 更新预计放下位置的预览
|
||
* @param {number} centerX - 预计放下的中心X坐标
|
||
* @param {number} centerY - 预计放下的中心Y坐标
|
||
*/
|
||
updateDragPreview(centerX, centerY) {
|
||
if (!this.dragPreview || !this.draggedItem) return;
|
||
|
||
const { width, height } = this.draggedItem.gridSize;
|
||
|
||
// 计算预计放下位置的左上角坐标
|
||
const topLeftX = Math.floor(centerX - (width - 1) / 2);
|
||
const topLeftY = Math.floor(centerY - (height - 1) / 2);
|
||
|
||
// 更新预览位置
|
||
const previewX = 10 + topLeftX * this.cellSize;
|
||
const previewY = 10 + topLeftY * this.cellSize;
|
||
this.dragPreview.setPosition(previewX, previewY);
|
||
|
||
// 根据是否可以放置更新预览颜色
|
||
const canPlace = this.inventory.canPlaceItem(this.draggedItem, centerX, centerY);
|
||
if (canPlace) {
|
||
this.dragPreview.setFillStyle(0x00ff00, 0.3);
|
||
this.dragPreview.setStrokeStyle(2, 0x00ff00, 1);
|
||
} else {
|
||
this.dragPreview.setFillStyle(0xff0000, 0.3);
|
||
this.dragPreview.setStrokeStyle(2, 0xff0000, 1);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 销毁预计放下位置的预览
|
||
*/
|
||
destroyDragPreview() {
|
||
if (this.dragPreview) {
|
||
this.dragPreview.destroy();
|
||
this.dragPreview = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新拖动时的悬停状态和预览位置
|
||
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||
*/
|
||
updateDragHover(pointer) {
|
||
// 计算鼠标在背包容器内的位置
|
||
const localX = pointer.x - this.container.x;
|
||
const localY = pointer.y - this.container.y;
|
||
|
||
// 计算对应的单元格坐标
|
||
const cellX = Math.floor((localX - 10) / this.cellSize);
|
||
const cellY = Math.floor((localY - 10) / this.cellSize);
|
||
|
||
// 检查坐标是否在背包范围内
|
||
if (cellX >= 0 && cellX < this.inventory.width && cellY >= 0 && cellY < this.inventory.height) {
|
||
// 更新悬停单元格
|
||
if (!this.hoveredCell || this.hoveredCell.x !== cellX || this.hoveredCell.y !== cellY) {
|
||
// 移除之前的悬停效果
|
||
if (this.hoveredCell) {
|
||
this.onCellUnhovered();
|
||
}
|
||
// 设置新的悬停单元格
|
||
this.onCellHovered(cellX, cellY);
|
||
}
|
||
|
||
// 更新预计放下位置的预览
|
||
this.updateDragPreview(cellX, cellY);
|
||
|
||
// 确保预览可见
|
||
if (this.dragPreview) {
|
||
this.dragPreview.setVisible(true);
|
||
}
|
||
} else {
|
||
// 鼠标移出背包范围,移除悬停效果
|
||
if (this.hoveredCell) {
|
||
this.onCellUnhovered();
|
||
}
|
||
|
||
// 隐藏预览
|
||
if (this.dragPreview) {
|
||
this.dragPreview.setVisible(false);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 全局鼠标释放事件处理
|
||
* @param {Phaser.Input.Pointer} pointer - 指针对象
|
||
*/
|
||
onMouseUp(pointer) {
|
||
// 如果正在拖动,处理拖动结束
|
||
if (this.isDragging && this.draggedItem) {
|
||
console.log('onMouseUp: 结束拖动');
|
||
|
||
// 计算鼠标在背包容器内的位置
|
||
const localX = pointer.x - this.container.x;
|
||
const localY = pointer.y - this.container.y;
|
||
|
||
// 计算对应的单元格坐标
|
||
const cellX = Math.floor((localX - 10) / this.cellSize);
|
||
const cellY = Math.floor((localY - 10) / this.cellSize);
|
||
|
||
// 尝试将物品放置到新位置
|
||
let success = false;
|
||
|
||
// 检查坐标是否在背包范围内
|
||
if (cellX >= 0 && cellX < this.inventory.width && cellY >= 0 && cellY < this.inventory.height) {
|
||
const targetItem = this.inventory.getItemAt(cellX, cellY);
|
||
|
||
// 检查新位置是否与旧位置完全相同
|
||
const isSamePosition = this.draggedItem.position.x === cellX && this.draggedItem.position.y === cellY;
|
||
|
||
// 如果目标位置是物品原来的位置且位置完全相同,直接取消拖动
|
||
if (targetItem && targetItem.id === this.draggedItem.id && isSamePosition) {
|
||
console.log('拖动到原起始位置,直接取消拖动');
|
||
success = true;
|
||
} else if (targetItem) {
|
||
if (targetItem.id === this.draggedItem.id) {
|
||
// 如果目标位置是当前拖动物品的非起始位置,尝试移动
|
||
console.log('拖动到当前物品的非起始位置,尝试移动:', this.draggedItem.name, '到位置:', { cellX, cellY });
|
||
success = this.inventory.moveItem(this.draggedItem.id, cellX, cellY);
|
||
console.log('移动结果:', success);
|
||
} else {
|
||
// 如果目标位置有其他物品,尝试交换
|
||
success = this.inventory.swapItems(this.draggedItem.id, targetItem.id);
|
||
}
|
||
} else {
|
||
// 如果目标位置是空的,尝试移动
|
||
success = this.inventory.moveItem(this.draggedItem.id, cellX, cellY);
|
||
}
|
||
} else {
|
||
// 鼠标释放位置不在背包范围内,执行丢弃操作
|
||
console.log('拖动出背包,执行丢弃操作:', this.draggedItem.name);
|
||
|
||
// 获取物品ID和基本信息
|
||
const itemId = this.draggedItem.itemTypeId || this.draggedItem.id;
|
||
|
||
// 从背包中移除物品
|
||
const removedItem = this.inventory.removeItem(this.draggedItem.id);
|
||
|
||
if (removedItem) {
|
||
// 在角色位置创建该物品
|
||
if (this.scene.itemManager) {
|
||
const player = this.scene.player;
|
||
this.scene.itemManager.createItem(itemId, player.x, player.y);
|
||
console.log('已在角色位置创建丢弃的物品:', removedItem.name);
|
||
}
|
||
|
||
success = true;
|
||
}
|
||
}
|
||
|
||
// 销毁预计放下位置的预览
|
||
this.destroyDragPreview();
|
||
|
||
// 重置拖动状态
|
||
this.isDragging = false;
|
||
if (this.draggedSprite) {
|
||
this.draggedSprite.setDepth(1); // 恢复精灵深度
|
||
this.draggedSprite = null;
|
||
}
|
||
|
||
// 更新UI
|
||
if (success) {
|
||
this.deselectItem();
|
||
this.updateUI();
|
||
} else {
|
||
// 如果移动失败,重置精灵位置
|
||
this.updateUI();
|
||
}
|
||
|
||
this.draggedItem = null;
|
||
this.spriteCenterOffset = null;
|
||
|
||
// 移除悬停效果
|
||
if (this.hoveredCell) {
|
||
this.onCellUnhovered();
|
||
}
|
||
}
|
||
|
||
// 重置点击状态
|
||
this.clickItem = null;
|
||
this.clickPosition = null;
|
||
this.clickTime = null;
|
||
}
|
||
|
||
/**
|
||
* 单元格点击事件
|
||
* @param {number} x - 单元格X坐标
|
||
* @param {number} y - 单元格Y坐标
|
||
*/
|
||
onCellClicked(x, y) {
|
||
console.log('onCellClicked:', { x, y });
|
||
const item = this.inventory.getItemAt(x, y);
|
||
|
||
if (this.selectedItem) {
|
||
console.log('已有选中物品:', this.selectedItem.name, 'ID:', this.selectedItem.id);
|
||
|
||
// 如果点击的是已选中物品,直接取消选择(无论点击的是哪个位置)
|
||
if (item && item.id === this.selectedItem.id) {
|
||
console.log('点击已选中物品,取消选择:', item.name);
|
||
this.deselectItem();
|
||
return;
|
||
}
|
||
|
||
// 尝试移动或交换物品
|
||
let success = false;
|
||
|
||
if (item) {
|
||
// 如果点击的是另一个物品,尝试交换
|
||
console.log('点击另一个物品,尝试交换:', item.name, 'ID:', item.id);
|
||
success = this.inventory.swapItems(this.selectedItem.id, item.id);
|
||
console.log('交换结果:', success);
|
||
} else {
|
||
// 如果点击的是空位,尝试移动
|
||
console.log('点击空位,尝试移动物品:', this.selectedItem.name, '到位置:', { x, y });
|
||
success = this.inventory.moveItem(this.selectedItem.id, x, y);
|
||
console.log('移动结果:', success);
|
||
}
|
||
|
||
if (success) {
|
||
// 只有操作成功时才取消选择并更新UI
|
||
console.log('操作成功,取消选择并更新UI');
|
||
this.deselectItem();
|
||
this.updateUI();
|
||
} else {
|
||
console.log('操作失败,保持选择状态');
|
||
}
|
||
} else if (item) {
|
||
// 如果没有选中的物品,选中点击的物品
|
||
console.log('没有选中物品,选中点击的物品:', item.name);
|
||
this.selectItem(item);
|
||
} else {
|
||
console.log('点击空位,没有选中物品,无操作');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 单元格悬停事件
|
||
* @param {number} x - 单元格X坐标
|
||
* @param {number} y - 单元格Y坐标
|
||
*/
|
||
onCellHovered(x, y) {
|
||
this.hoveredCell = { x, y };
|
||
|
||
// 高亮显示悬停的单元格
|
||
this.gridCells[y][x].setFillStyle(0x888888, 0.7);
|
||
|
||
// 只有在没有选中物品且不是拖动状态时,才不显示物品信息(根据需求,只在选中时显示)
|
||
// 注释掉显示物品信息的逻辑,只在选中时显示
|
||
/* const item = this.inventory.getItemAt(x, y);
|
||
if (item) {
|
||
this.showItemInfo(item);
|
||
} */
|
||
}
|
||
|
||
/**
|
||
* 单元格取消悬停事件
|
||
*/
|
||
onCellUnhovered() {
|
||
if (this.hoveredCell) {
|
||
// 恢复单元格颜色
|
||
const { x, y } = this.hoveredCell;
|
||
this.gridCells[y][x].setFillStyle(0x666666, 0.5);
|
||
this.hoveredCell = null;
|
||
}
|
||
|
||
// 如果有选中物品,不要隐藏物品信息
|
||
if (!this.selectedItem) {
|
||
// 隐藏物品信息
|
||
this.hideItemInfo();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 选择物品
|
||
* @param {Object} item - 要选择的物品
|
||
*/
|
||
selectItem(item) {
|
||
this.selectedItem = item;
|
||
|
||
// 高亮显示物品边框,不改变单元格颜色
|
||
const spriteObj = this.itemSprites.get(item.id);
|
||
if (spriteObj) {
|
||
spriteObj.border.setStrokeStyle(2, 0xffff00, 1); // 黄色边框表示选中
|
||
}
|
||
|
||
// 显示选中物品的详情
|
||
this.showItemInfo(item);
|
||
console.log('选中物品:', item.name);
|
||
|
||
// 如果是种子类型的物品,显示使用提示
|
||
if (item.type === 'seed') {
|
||
this.scene.events.emit('show-message', `右键点击、按Q键或点击[使用]按钮可以种植种子`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 取消选择物品
|
||
*/
|
||
deselectItem() {
|
||
if (!this.selectedItem) return;
|
||
|
||
// 恢复物品边框颜色,不改变单元格颜色
|
||
const spriteObj = this.itemSprites.get(this.selectedItem.id);
|
||
if (spriteObj) {
|
||
spriteObj.border.setStrokeStyle(1, 0xffffff, 1); // 恢复白色边框
|
||
}
|
||
|
||
// 隐藏物品详情
|
||
this.hideItemInfo();
|
||
console.log('取消选择物品:', this.selectedItem.name);
|
||
this.selectedItem = null;
|
||
}
|
||
|
||
/**
|
||
* 显示物品信息
|
||
* @param {Object} item - 物品
|
||
*/
|
||
showItemInfo(item) {
|
||
// 创建物品信息面板
|
||
this.itemInfoPanel = this.scene.add.rectangle(0, 0, 200, 150, 0x000000, 0.8);
|
||
this.itemInfoPanel.setOrigin(0, 0);
|
||
this.itemInfoPanel.setStrokeStyle(1, 0xffffff);
|
||
this.itemInfoPanel.setPosition(
|
||
this.container.x + this.inventory.width * this.cellSize + 50,
|
||
this.container.y + this.inventory.height * this.cellSize + 20
|
||
);
|
||
|
||
// 设置足够高的深度,确保面板显示在最上层
|
||
this.itemInfoPanel.setDepth(150);
|
||
|
||
// 阻止事件冒泡,防止点击物品信息面板时触发角色移动
|
||
this.itemInfoPanel.setInteractive();
|
||
this.itemInfoPanel.on('pointerdown', (pointer) => {
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
// 文本本身不需要交互,只需要面板阻止事件即可
|
||
// 如果需要文本也阻止事件,可以添加setInteractive和事件处理
|
||
this.itemInfoPanel.setScrollFactor(0); // 固定在屏幕上
|
||
|
||
// 添加物品信息文本
|
||
this.itemInfoText = this.scene.add.text(
|
||
this.itemInfoPanel.x + 10,
|
||
this.itemInfoPanel.y + 10,
|
||
`${item.name}\n${item.description}\n重量: ${item.weight}\n价值: ${item.value}`,
|
||
{ font: '12px Arial', fill: '#ffffff', wordWrap: { width: 180 } }
|
||
);
|
||
this.itemInfoText.setScrollFactor(0); // 固定在屏幕上
|
||
this.itemInfoText.setDepth(160); // 设置比面板更高的深度
|
||
|
||
// 添加"使用"按钮
|
||
const useButton = this.scene.add.text(
|
||
this.itemInfoPanel.x + 10,
|
||
this.itemInfoPanel.y + 90,
|
||
'[使用]',
|
||
{ font: '14px Arial', fill: '#00ff00' }
|
||
);
|
||
useButton.setInteractive({
|
||
useHandCursor: true
|
||
});
|
||
useButton.setDepth(170); // 设置比文本更高的深度
|
||
|
||
// 存储当前物品引用,确保回调函数中this指向正确
|
||
const currentItem = item;
|
||
const inventoryUI = this;
|
||
|
||
useButton.on('pointerdown', (pointer) => {
|
||
console.log('点击使用按钮,使用物品:', currentItem.name);
|
||
inventoryUI.useItem(currentItem);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
useButton.on('pointerover', (pointer) => {
|
||
useButton.setFill('#ffff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
useButton.on('pointerout', (pointer) => {
|
||
useButton.setFill('#00ff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
useButton.setScrollFactor(0); // 固定在屏幕上
|
||
|
||
// 如果是种子类型的物品,添加"种植/取消种植"按钮
|
||
// 如果是作物类型的物品,添加"制作种子"按钮
|
||
let actionButton = null;
|
||
let buttonX = this.itemInfoPanel.x + 80;
|
||
let buttonY = this.itemInfoPanel.y + 90;
|
||
|
||
if (item.type === 'seed') {
|
||
// 检查当前是否处于种植模式
|
||
const isPlantingMode = this.scene.isPlantingMode || false;
|
||
const buttonText = isPlantingMode ? '[取消种植]' : '[种植]';
|
||
const buttonColor = isPlantingMode ? '#ff0000' : '#00ff00';
|
||
|
||
actionButton = this.scene.add.text(
|
||
buttonX,
|
||
buttonY,
|
||
buttonText,
|
||
{ font: '14px Arial', fill: buttonColor }
|
||
);
|
||
// 设置足够高的深度,确保按钮可以点击
|
||
actionButton.setDepth(200);
|
||
actionButton.setScrollFactor(0); // 固定在屏幕上
|
||
actionButton.setInteractive({
|
||
useHandCursor: true,
|
||
draggable: false,
|
||
cursor: 'pointer'
|
||
});
|
||
|
||
// 确保事件监听正确绑定
|
||
actionButton.removeAllListeners();
|
||
|
||
actionButton.on('pointerdown', (pointer) => {
|
||
console.log('DEBUG: 种植/取消种植按钮点击事件触发');
|
||
if (isPlantingMode) {
|
||
// 如果是种植模式,调用取消种植
|
||
console.log('点击取消种植按钮,取消种植:', currentItem.name);
|
||
this.scene.cancelPlantingMode();
|
||
} else {
|
||
// 否则,使用种子开始种植
|
||
console.log('点击种植按钮,使用种子:', currentItem.name);
|
||
inventoryUI.useItem(currentItem);
|
||
}
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
actionButton.on('pointerover', (pointer) => {
|
||
actionButton.setFill('#ffff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
actionButton.on('pointerout', (pointer) => {
|
||
actionButton.setFill(buttonColor);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
console.log('DEBUG: 种植/取消种植按钮创建完成,深度:', actionButton.depth);
|
||
} else if (item.type === 'food' && ['wheat', 'carrot'].includes(item.itemTypeId)) {
|
||
// 如果是作物类型的物品,添加"制作种子"按钮
|
||
const buttonText = '[制作种子]';
|
||
const buttonColor = '#0080ff';
|
||
|
||
actionButton = this.scene.add.text(
|
||
buttonX,
|
||
buttonY,
|
||
buttonText,
|
||
{ font: '14px Arial', fill: buttonColor }
|
||
);
|
||
// 设置足够高的深度,确保按钮可以点击
|
||
actionButton.setDepth(200);
|
||
actionButton.setScrollFactor(0); // 固定在屏幕上
|
||
actionButton.setInteractive({
|
||
useHandCursor: true,
|
||
draggable: false,
|
||
cursor: 'pointer'
|
||
});
|
||
|
||
// 确保事件监听正确绑定
|
||
actionButton.removeAllListeners();
|
||
|
||
actionButton.on('pointerdown', (pointer) => {
|
||
console.log('DEBUG: 制作种子按钮点击事件触发');
|
||
inventoryUI.craftSeeds(currentItem);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
actionButton.on('pointerover', (pointer) => {
|
||
actionButton.setFill('#ffff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
actionButton.on('pointerout', (pointer) => {
|
||
actionButton.setFill(buttonColor);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
console.log('DEBUG: 制作种子按钮创建完成,深度:', actionButton.depth);
|
||
}
|
||
|
||
// 存储按钮引用,以便后续销毁
|
||
this.itemInfoButton = useButton;
|
||
this.plantButton = actionButton;
|
||
|
||
this.container.add(this.itemInfoPanel);
|
||
this.container.add(this.itemInfoText);
|
||
this.container.add(useButton);
|
||
|
||
// 如果有额外按钮(种植/制作种子),添加到容器中
|
||
if (actionButton) {
|
||
this.container.add(actionButton);
|
||
}
|
||
|
||
// 添加拾取按钮
|
||
const dropButton = this.scene.add.text(
|
||
this.itemInfoPanel.x + 10,
|
||
this.itemInfoPanel.y + 115,
|
||
'[丢弃]',
|
||
{ font: '14px Arial', fill: '#ff0000' }
|
||
);
|
||
dropButton.setInteractive({
|
||
useHandCursor: true
|
||
});
|
||
dropButton.setDepth(170); // 设置比文本更高的深度
|
||
|
||
dropButton.on('pointerdown', (pointer) => {
|
||
console.log('点击丢弃按钮,丢弃物品:', currentItem.name);
|
||
inventoryUI.dropItem(currentItem);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
dropButton.on('pointerover', (pointer) => {
|
||
dropButton.setFill('#ffff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
dropButton.on('pointerout', (pointer) => {
|
||
dropButton.setFill('#ff0000');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
dropButton.setScrollFactor(0); // 固定在屏幕上
|
||
this.container.add(dropButton);
|
||
|
||
// 存储拾取按钮引用,以便后续销毁
|
||
this.dropButton = dropButton;
|
||
}
|
||
|
||
/**
|
||
* 更新种植按钮状态
|
||
* @param {boolean} isPlanting - 是否处于种植模式
|
||
*/
|
||
updatePlantButtonState(isPlanting) {
|
||
// 如果有显示的物品信息且是种子类型,更新按钮状态
|
||
if (this.itemInfoPanel && this.plantButton) {
|
||
console.log('DEBUG: updatePlantButtonState 方法被调用,isPlanting:', isPlanting);
|
||
// 设置足够高的深度,确保按钮可以点击
|
||
this.plantButton.setDepth(200);
|
||
// 确保按钮可见
|
||
this.plantButton.setVisible(true);
|
||
// 更新按钮文本和颜色
|
||
this.plantButton.setText(isPlanting ? '[取消种植]' : '[种植]');
|
||
this.plantButton.setFill(isPlanting ? '#ff0000' : '#00ff00');
|
||
|
||
// 重新绑定点击事件
|
||
const currentItem = this.selectedItem;
|
||
const inventoryUI = this;
|
||
|
||
// 移除旧的事件监听器
|
||
this.plantButton.removeAllListeners();
|
||
|
||
// 确保按钮可以交互
|
||
this.plantButton.setInteractive({
|
||
useHandCursor: true,
|
||
draggable: false,
|
||
cursor: 'pointer'
|
||
});
|
||
|
||
// 重新添加事件监听器
|
||
this.plantButton.on('pointerdown', (pointer) => {
|
||
console.log('DEBUG: 更新后的种植/取消种植按钮点击事件触发');
|
||
if (isPlanting) {
|
||
// 如果是种植模式,调用取消种植
|
||
console.log('点击取消种植按钮,取消种植:', currentItem.name);
|
||
inventoryUI.scene.cancelPlantingMode();
|
||
} else {
|
||
// 否则,使用种子开始种植
|
||
console.log('点击种植按钮,使用种子:', currentItem.name);
|
||
inventoryUI.useItem(currentItem);
|
||
}
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
this.plantButton.on('pointerover', (pointer) => {
|
||
this.plantButton.setFill('#ffff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
this.plantButton.on('pointerout', (pointer) => {
|
||
this.plantButton.setFill(isPlanting ? '#ff0000' : '#00ff00');
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
console.log('DEBUG: 种植/取消种植按钮状态更新完成');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏物品信息
|
||
*/
|
||
hideItemInfo() {
|
||
if (this.itemInfoPanel) {
|
||
this.itemInfoPanel.destroy();
|
||
this.itemInfoText.destroy();
|
||
if (this.itemInfoButton) {
|
||
this.itemInfoButton.destroy();
|
||
this.itemInfoButton = null;
|
||
}
|
||
if (this.plantButton) {
|
||
this.plantButton.destroy();
|
||
this.plantButton = null;
|
||
}
|
||
if (this.dropButton) {
|
||
this.dropButton.destroy();
|
||
this.dropButton = null;
|
||
}
|
||
this.itemInfoPanel = null;
|
||
this.itemInfoText = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 丢弃物品
|
||
* @param {Object} item - 要丢弃的物品
|
||
*/
|
||
dropItem(item) {
|
||
console.log('dropItem:', item.name, 'ID:', item.id);
|
||
|
||
// 从背包中移除物品
|
||
const removedItem = this.inventory.removeItem(item.id);
|
||
if (!removedItem) {
|
||
console.log('无法从背包中移除物品:', item.name);
|
||
return;
|
||
}
|
||
|
||
// 获取物品ID和基本信息
|
||
const itemId = removedItem.itemTypeId || removedItem.id;
|
||
|
||
// 在角色位置创建该物品
|
||
if (this.scene.itemManager) {
|
||
const player = this.scene.player;
|
||
this.scene.itemManager.createItem(itemId, player.x, player.y);
|
||
console.log('已在角色位置创建丢弃的物品:', removedItem.name);
|
||
}
|
||
|
||
// 更新UI显示
|
||
this.updateUI();
|
||
|
||
// 显示丢弃成功消息
|
||
this.scene.events.emit('show-message', `已丢弃 ${removedItem.name}`);
|
||
|
||
// 取消选择物品
|
||
this.deselectItem();
|
||
}
|
||
|
||
/**
|
||
* 更新UI显示
|
||
*/
|
||
updateUI() {
|
||
// 移除所有物品精灵和边框
|
||
this.itemSprites.forEach(spriteObj => {
|
||
spriteObj.sprite.destroy();
|
||
spriteObj.border.destroy();
|
||
});
|
||
this.itemSprites.clear();
|
||
|
||
// 重新添加所有物品精灵
|
||
const items = this.inventory.getAllItems();
|
||
if (items instanceof Map) {
|
||
// 如果是Map对象,使用Map的forEach方法
|
||
items.forEach(item => {
|
||
this.addItemSprite(item);
|
||
});
|
||
} else {
|
||
// 如果是数组,使用数组的forEach方法
|
||
items.forEach(item => {
|
||
this.addItemSprite(item);
|
||
});
|
||
}
|
||
|
||
// 更新统计信息
|
||
this.updateStats();
|
||
}
|
||
|
||
/**
|
||
* 添加物品精灵(基于物品中心坐标)
|
||
* @param {Object} item - 物品
|
||
*/
|
||
addItemSprite(item) {
|
||
// 获取物品中心坐标和尺寸
|
||
const { x: centerX, y: centerY } = item.position;
|
||
const { width, height } = item.gridSize;
|
||
|
||
// 计算左上角坐标
|
||
const topLeftX = Math.floor(centerX - (width - 1) / 2);
|
||
const topLeftY = Math.floor(centerY - (height - 1) / 2);
|
||
|
||
// 计算精灵位置和大小
|
||
const spriteX = 10 + topLeftX * this.cellSize;
|
||
const spriteY = 10 + topLeftY * this.cellSize;
|
||
const spriteWidth = width * this.cellSize;
|
||
const spriteHeight = height * this.cellSize;
|
||
|
||
// 创建精灵 - 使用物品类型ID而不是唯一ID,因为纹理是用物品类型ID创建的
|
||
const sprite = this.scene.add.sprite(spriteX, spriteY, item.itemTypeId || item.id);
|
||
sprite.setOrigin(0, 0);
|
||
sprite.setDisplaySize(spriteWidth, spriteHeight);
|
||
sprite.setScrollFactor(0); // 固定在屏幕上
|
||
sprite.setDepth(1); // 确保精灵在单元格之上
|
||
|
||
// 添加边框
|
||
const border = this.scene.add.rectangle(spriteX, spriteY, spriteWidth, spriteHeight, 0xffffff, 0);
|
||
border.setOrigin(0, 0);
|
||
border.setStrokeStyle(1, 0xffffff);
|
||
border.setScrollFactor(0); // 固定在屏幕上
|
||
border.setDepth(2); // 确保边框在精灵之上
|
||
|
||
// 处理物品精灵的点击事件
|
||
sprite.setInteractive();
|
||
sprite.on('pointerdown', (pointer) => {
|
||
this.onItemSpriteDown(item, pointer);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
border.setInteractive();
|
||
border.on('pointerdown', (pointer) => {
|
||
this.onItemSpriteDown(item, pointer);
|
||
return false; // 阻止事件冒泡
|
||
});
|
||
|
||
this.container.add(sprite);
|
||
this.container.add(border);
|
||
this.itemSprites.set(item.id, { sprite, border });
|
||
}
|
||
|
||
/**
|
||
* 显示背包
|
||
*/
|
||
show() {
|
||
this.container.setVisible(true);
|
||
this.isOpen = true;
|
||
this.updateUI();
|
||
}
|
||
|
||
/**
|
||
* 隐藏背包
|
||
*/
|
||
hide() {
|
||
this.container.setVisible(false);
|
||
this.isOpen = false;
|
||
this.deselectItem();
|
||
this.hideItemInfo();
|
||
}
|
||
|
||
/**
|
||
* 切换背包显示
|
||
*/
|
||
toggle() {
|
||
if (this.isOpen) {
|
||
this.hide();
|
||
} else {
|
||
this.show();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 添加物品到背包并更新UI
|
||
* @param {Object} item - 要添加的物品
|
||
* @returns {boolean} 是否添加成功
|
||
*/
|
||
addItem(item) {
|
||
const success = this.inventory.addItem(item);
|
||
if (success) {
|
||
this.updateUI();
|
||
}
|
||
return success;
|
||
}
|
||
|
||
/**
|
||
* 从背包中移除物品并更新UI
|
||
* @param {string} itemId - 物品ID
|
||
* @returns {Object|null} 移除的物品
|
||
*/
|
||
removeItem(itemId) {
|
||
const item = this.inventory.removeItem(itemId);
|
||
if (item) {
|
||
this.updateUI();
|
||
}
|
||
return item;
|
||
}
|
||
|
||
/**
|
||
* 制作种子
|
||
* @param {Object} item - 要制作种子的作物
|
||
*/
|
||
craftSeeds(item) {
|
||
console.log('craftSeeds:', item.name, 'ID:', item.id);
|
||
|
||
// 检查物品是否可以制作种子
|
||
if (item.type !== 'food' || !['wheat', 'carrot'].includes(item.itemTypeId)) {
|
||
console.log('该物品不能制作种子:', item.name);
|
||
return;
|
||
}
|
||
|
||
// 从背包中移除作物
|
||
const removedItem = this.inventory.removeItem(item.id);
|
||
if (!removedItem) {
|
||
console.log('无法从背包中移除作物:', item.name);
|
||
return;
|
||
}
|
||
|
||
// 制作种子数量(1个作物制作2个种子)
|
||
const seedsCount = 2;
|
||
|
||
// 获取对应作物的种子ID
|
||
const seedId = `${item.itemTypeId}_seed`;
|
||
|
||
// 使用ItemManager创建种子物品
|
||
if (this.scene.itemManager) {
|
||
let successfullyAdded = 0;
|
||
let seedsToDrop = [];
|
||
|
||
// 先创建所有种子
|
||
for (let i = 0; i < seedsCount; i++) {
|
||
// 创建种子物品
|
||
const seedItem = this.scene.itemManager.createItem(seedId, 0, 0);
|
||
if (seedItem) {
|
||
// 尝试将种子添加到背包
|
||
const success = this.inventory.addItem(seedItem);
|
||
if (success) {
|
||
successfullyAdded++;
|
||
} else {
|
||
// 如果背包空间不足,将种子添加到待丢弃列表
|
||
seedsToDrop.push(seedItem);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果背包空间不足,将无法放入背包的种子放到地图上
|
||
if (seedsToDrop.length > 0 && this.scene.player) {
|
||
const player = this.scene.player;
|
||
seedsToDrop.forEach(seedItem => {
|
||
// 设置种子位置为玩家位置
|
||
seedItem.x = player.x;
|
||
seedItem.y = player.y;
|
||
// 确保种子被正确添加到场景中
|
||
this.scene.add.existing(seedItem);
|
||
console.log('背包空间不足,已将种子放到地图上:', seedItem.name);
|
||
});
|
||
}
|
||
|
||
// 更新UI显示
|
||
this.updateUI();
|
||
|
||
// 显示制作成功消息
|
||
let message = `成功制作了${successfullyAdded}个${seedId.replace('_seed', '')}种子`;
|
||
if (seedsToDrop.length > 0) {
|
||
message += `,${seedsToDrop.length}个种子因背包空间不足已放到地上`;
|
||
}
|
||
this.scene.events.emit('show-message', message);
|
||
|
||
console.log(`成功制作了${successfullyAdded}个${seedId.replace('_seed', '')}种子,${seedsToDrop.length}个种子放到了地上`);
|
||
} else {
|
||
console.log('场景中没有ItemManager,无法制作种子');
|
||
// 如果无法制作种子,将作物归还
|
||
this.inventory.addItem(item);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 销毁背包UI
|
||
*/
|
||
destroy() {
|
||
this.container.destroy();
|
||
}
|
||
} |