- Draw blue lines along each spoke - Show circles at spoke endpoints - Visualize spoke positioning and angles 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
342 lines
13 KiB
JavaScript
342 lines
13 KiB
JavaScript
import Phaser from 'phaser';
|
|
|
|
export default class ShipDeckScene extends Phaser.Scene {
|
|
constructor() {
|
|
super({ key: 'ShipDeckScene' });
|
|
this.inventory = {
|
|
whaleOil: 0,
|
|
fuel: 100,
|
|
penguins: 0
|
|
};
|
|
}
|
|
|
|
init(data) {
|
|
// Receive inventory data when returning from other scenes
|
|
if (data && data.inventory) {
|
|
this.inventory = data.inventory;
|
|
}
|
|
}
|
|
|
|
create() {
|
|
// Background - ship deck
|
|
this.add.rectangle(400, 300, 800, 600, 0x8B4513);
|
|
this.add.text(400, 30, 'The Deck - Yer Whaling Vessel', {
|
|
fontSize: '24px',
|
|
fill: '#fff'
|
|
}).setOrigin(0.5);
|
|
|
|
// Create interactive areas
|
|
this.createDeck();
|
|
this.createBarrels();
|
|
this.createWheel();
|
|
this.createInventoryDisplay();
|
|
this.createMessageBox();
|
|
|
|
// Instructions
|
|
this.add.text(400, 560, 'Click on things to have a look, ye scurvy dog', {
|
|
fontSize: '16px',
|
|
fill: '#ffff99'
|
|
}).setOrigin(0.5);
|
|
}
|
|
|
|
createDeck() {
|
|
// Ship deck dimensions
|
|
this.deckCenterX = 400;
|
|
this.deckWidth = 700;
|
|
this.deckTopY = 200;
|
|
this.deckPlankSpacing = 30;
|
|
this.deckPlankCount = 8;
|
|
|
|
// Calculate deck boundaries
|
|
this.deckLeftX = this.deckCenterX - this.deckWidth / 2;
|
|
this.deckRightX = this.deckCenterX + this.deckWidth / 2;
|
|
this.deckBottomY = this.deckTopY + (this.deckPlankCount - 1) * this.deckPlankSpacing;
|
|
|
|
// Ship deck planks
|
|
for (let i = 0; i < this.deckPlankCount; i++) {
|
|
this.add.rectangle(this.deckCenterX, this.deckTopY + i * this.deckPlankSpacing, this.deckWidth, 25, 0x654321)
|
|
.setStrokeStyle(2, 0x3d2817);
|
|
}
|
|
|
|
// Calculate actual deck edges (including plank height)
|
|
const plankHeight = 25;
|
|
this.deckTopEdge = this.deckTopY - (plankHeight / 2);
|
|
this.deckBottomEdge = this.deckBottomY + (plankHeight / 2);
|
|
const deckHeight = this.deckBottomEdge - this.deckTopEdge;
|
|
const deckCenterY = (this.deckTopEdge + this.deckBottomEdge) / 2;
|
|
|
|
const deckBorder = this.add.rectangle(this.deckCenterX, deckCenterY, this.deckWidth, deckHeight);
|
|
deckBorder.setStrokeStyle(4, 0x3d2817);
|
|
deckBorder.setFillStyle(0x000000, 0); // Transparent fill
|
|
}
|
|
|
|
createBarrels() {
|
|
// Initialize barrel arrays
|
|
this.fuelBarrels = [];
|
|
this.oilBarrels = [];
|
|
|
|
// Create stacked barrels
|
|
this.createFuelBarrels();
|
|
this.createOilBarrels();
|
|
|
|
// Penguin cage (hidden until discovered)
|
|
this.penguinCage = this.add.rectangle(650, 350, 80, 80, 0x333333);
|
|
this.penguinCage.setInteractive({ useHandCursor: true });
|
|
this.penguinCage.setStrokeStyle(3, 0x000000);
|
|
|
|
this.penguinEmoji = this.add.text(650, 350, '🐧', {
|
|
fontSize: '32px'
|
|
}).setOrigin(0.5);
|
|
|
|
// Hide penguin elements if not yet discovered
|
|
const discovered = this.inventory.penguins > 0;
|
|
this.penguinCage.setVisible(discovered);
|
|
this.penguinEmoji.setVisible(discovered);
|
|
|
|
this.penguinCage.on('pointerdown', () => {
|
|
if (this.inventory.penguins > 0) {
|
|
this.showMessage(`Arr, ${this.inventory.penguins} penguins in the hold. Emergency fuel, they be. Desperate times call fer desperate measures, savvy?`);
|
|
} else {
|
|
this.showMessage(`Empty penguin cage, aye. Should we be catchin' some fer... fuel?`);
|
|
}
|
|
});
|
|
}
|
|
|
|
createFuelBarrels() {
|
|
const barrelCount = Math.ceil(this.inventory.fuel / 10);
|
|
if (barrelCount === 0) return;
|
|
|
|
const barrelWidth = 50;
|
|
const barrelHeight = 70;
|
|
const horizontalSpacing = 60;
|
|
const verticalSpacing = 75;
|
|
const barrelsPerRow = 5;
|
|
|
|
// Calculate group dimensions
|
|
const groupWidth = (Math.min(barrelCount, barrelsPerRow) * horizontalSpacing);
|
|
const stackLevels = Math.ceil(barrelCount / barrelsPerRow);
|
|
|
|
// Center horizontally in left half of deck
|
|
const leftHalfCenter = this.deckLeftX + (this.deckWidth / 4);
|
|
const baseX = leftHalfCenter - (groupWidth / 2) + (horizontalSpacing / 2);
|
|
|
|
// Center vertically on deck (using actual deck edges)
|
|
const deckVerticalCenter = (this.deckTopEdge + this.deckBottomEdge) / 2;
|
|
// Calculate where bottom barrel should be so group is centered
|
|
// Top of group = baseY - (stackLevels-1)*verticalSpacing - barrelHeight/2
|
|
// Bottom of group = baseY + barrelHeight/2
|
|
// Center of group = (top + bottom) / 2 = baseY - ((stackLevels-1)*verticalSpacing)/2
|
|
// We want: baseY - ((stackLevels-1)*verticalSpacing)/2 = deckVerticalCenter
|
|
const baseY = deckVerticalCenter + ((stackLevels - 1) * verticalSpacing) / 2;
|
|
|
|
// Create barrels in stacking pattern
|
|
for (let i = 0; i < barrelCount; i++) {
|
|
const row = i % barrelsPerRow;
|
|
const stackLevel = Math.floor(i / barrelsPerRow);
|
|
const x = baseX + (row * horizontalSpacing);
|
|
const y = baseY - (stackLevel * verticalSpacing);
|
|
|
|
const barrel = this.add.rectangle(x, y, barrelWidth, barrelHeight, 0x8B0000);
|
|
barrel.setStrokeStyle(3, 0x000000);
|
|
this.fuelBarrels.push(barrel);
|
|
|
|
// Add label only on first barrel
|
|
if (i === 0) {
|
|
const label = this.add.text(x, y, 'FUEL', {
|
|
fontSize: '12px',
|
|
fill: '#fff'
|
|
}).setOrigin(0.5);
|
|
this.fuelBarrels.push(label);
|
|
}
|
|
}
|
|
|
|
// Create interactive zone covering all fuel barrels
|
|
const zoneWidth = Math.min(barrelCount, barrelsPerRow) * horizontalSpacing + barrelWidth;
|
|
const zoneHeight = Math.ceil(barrelCount / barrelsPerRow) * verticalSpacing + barrelHeight;
|
|
const zoneX = baseX + ((Math.min(barrelCount, barrelsPerRow) - 1) * horizontalSpacing) / 2;
|
|
const zoneY = baseY - ((Math.ceil(barrelCount / barrelsPerRow) - 1) * verticalSpacing) / 2;
|
|
|
|
const interactiveZone = this.add.rectangle(zoneX, zoneY, zoneWidth, zoneHeight, 0x000000, 0);
|
|
interactiveZone.setInteractive({ useHandCursor: true });
|
|
interactiveZone.on('pointerdown', () => {
|
|
this.showMessage(`Arr! ${barrelCount} barrels of fuel (${this.inventory.fuel} units) fer boilin' down the blubber, aye!`);
|
|
});
|
|
this.fuelBarrels.push(interactiveZone);
|
|
}
|
|
|
|
createOilBarrels() {
|
|
const barrelCount = Math.ceil(this.inventory.whaleOil / 10);
|
|
if (barrelCount === 0) return;
|
|
|
|
const barrelWidth = 50;
|
|
const barrelHeight = 70;
|
|
const horizontalSpacing = 60;
|
|
const verticalSpacing = 75;
|
|
const barrelsPerRow = 3;
|
|
|
|
// Calculate group dimensions
|
|
const groupWidth = (Math.min(barrelCount, barrelsPerRow) * horizontalSpacing);
|
|
const stackLevels = Math.ceil(barrelCount / barrelsPerRow);
|
|
|
|
// Center horizontally in right half of deck
|
|
const rightHalfCenter = this.deckCenterX + (this.deckWidth / 4);
|
|
const baseX = rightHalfCenter - (groupWidth / 2) + (horizontalSpacing / 2);
|
|
|
|
// Center vertically on deck (using actual deck edges)
|
|
const deckVerticalCenter = (this.deckTopEdge + this.deckBottomEdge) / 2;
|
|
// Calculate where bottom barrel should be so group is centered
|
|
const baseY = deckVerticalCenter + ((stackLevels - 1) * verticalSpacing) / 2;
|
|
|
|
// Create barrels in stacking pattern
|
|
for (let i = 0; i < barrelCount; i++) {
|
|
const row = i % barrelsPerRow;
|
|
const stackLevel = Math.floor(i / barrelsPerRow);
|
|
const x = baseX + (row * horizontalSpacing);
|
|
const y = baseY - (stackLevel * verticalSpacing);
|
|
|
|
const barrel = this.add.rectangle(x, y, barrelWidth, barrelHeight, 0x4B4B00);
|
|
barrel.setStrokeStyle(3, 0x000000);
|
|
this.oilBarrels.push(barrel);
|
|
|
|
// Add label only on first barrel
|
|
if (i === 0) {
|
|
const label = this.add.text(x, y, 'OIL', {
|
|
fontSize: '12px',
|
|
fill: '#fff'
|
|
}).setOrigin(0.5);
|
|
this.oilBarrels.push(label);
|
|
}
|
|
}
|
|
|
|
// Create interactive zone covering all oil barrels
|
|
const zoneWidth = Math.min(barrelCount, barrelsPerRow) * horizontalSpacing + barrelWidth;
|
|
const zoneHeight = Math.ceil(barrelCount / barrelsPerRow) * verticalSpacing + barrelHeight;
|
|
const zoneX = baseX + ((Math.min(barrelCount, barrelsPerRow) - 1) * horizontalSpacing) / 2;
|
|
const zoneY = baseY - ((Math.ceil(barrelCount / barrelsPerRow) - 1) * verticalSpacing) / 2;
|
|
|
|
const interactiveZone = this.add.rectangle(zoneX, zoneY, zoneWidth, zoneHeight, 0x000000, 0);
|
|
interactiveZone.setInteractive({ useHandCursor: true });
|
|
interactiveZone.on('pointerdown', () => {
|
|
this.showMessage(`Aye! ${barrelCount} barrels of whale oil (${this.inventory.whaleOil} units). Yer precious cargo, worth its weight in gold!`);
|
|
});
|
|
this.oilBarrels.push(interactiveZone);
|
|
}
|
|
|
|
clearBarrels() {
|
|
// Destroy all fuel barrels
|
|
this.fuelBarrels.forEach(barrel => barrel.destroy());
|
|
this.fuelBarrels = [];
|
|
|
|
// Destroy all oil barrels
|
|
this.oilBarrels.forEach(barrel => barrel.destroy());
|
|
this.oilBarrels = [];
|
|
}
|
|
|
|
createWheel() {
|
|
// Ship's wheel
|
|
const wheel = this.add.circle(550, 200, 40, 0x8B4513);
|
|
wheel.setInteractive({ useHandCursor: true });
|
|
wheel.setStrokeStyle(4, 0x654321);
|
|
|
|
// Wheel spokes
|
|
const spokeDebug = this.add.graphics();
|
|
spokeDebug.lineStyle(2, 0x0000FF, 0.8);
|
|
|
|
for (let i = 0; i < 8; i++) {
|
|
const angle = (i * 45) * Math.PI / 180;
|
|
const x1 = 550 + Math.cos(angle) * 15;
|
|
const y1 = 200 + Math.sin(angle) * 15;
|
|
const x2 = 550 + Math.cos(angle) * 35;
|
|
const y2 = 200 + Math.sin(angle) * 35;
|
|
this.add.line(0, 0, x1, y1, x2, y2, 0x654321).setLineWidth(3);
|
|
|
|
// Debug: Draw blue lines showing spoke endpoints
|
|
spokeDebug.lineBetween(x1, y1, x2, y2);
|
|
// Draw circles at endpoints
|
|
spokeDebug.strokeCircle(x1, y1, 3);
|
|
spokeDebug.strokeCircle(x2, y2, 3);
|
|
}
|
|
|
|
wheel.on('pointerdown', () => {
|
|
// Open the map scene
|
|
this.scene.start('MapScene', { inventory: this.inventory });
|
|
});
|
|
|
|
// Debug: Draw lines for wheel positioning
|
|
const debugLines = this.add.graphics();
|
|
debugLines.lineStyle(2, 0x00FF00, 0.8);
|
|
|
|
// Center crosshair
|
|
debugLines.lineBetween(550 - 50, 200, 550 + 50, 200); // Horizontal
|
|
debugLines.lineBetween(550, 200 - 50, 550, 200 + 50); // Vertical
|
|
|
|
// Bounding box (radius 40)
|
|
debugLines.strokeRect(550 - 40, 200 - 40, 80, 80);
|
|
}
|
|
|
|
createInventoryDisplay() {
|
|
// Inventory panel
|
|
const panel = this.add.rectangle(80, 80, 140, 100, 0x000000, 0.7);
|
|
panel.setStrokeStyle(2, 0xffffff);
|
|
|
|
this.inventoryText = this.add.text(20, 40, '', {
|
|
fontSize: '14px',
|
|
fill: '#fff'
|
|
});
|
|
|
|
this.updateInventoryDisplay();
|
|
}
|
|
|
|
updateInventoryDisplay() {
|
|
const lines = [
|
|
'Inventory:',
|
|
`Fuel: ${this.inventory.fuel}/100`,
|
|
`Oil: ${this.inventory.whaleOil}/50`
|
|
];
|
|
|
|
// Only show penguins if discovered (have at least one)
|
|
const discovered = this.inventory.penguins > 0;
|
|
if (discovered) {
|
|
lines.push(`Penguins: ${this.inventory.penguins}/20`);
|
|
}
|
|
|
|
this.inventoryText.setText(lines);
|
|
|
|
// Update penguin cage visibility
|
|
if (this.penguinCage) {
|
|
this.penguinCage.setVisible(discovered);
|
|
this.penguinEmoji.setVisible(discovered);
|
|
}
|
|
|
|
// Refresh barrel visualization
|
|
this.clearBarrels();
|
|
this.createFuelBarrels();
|
|
this.createOilBarrels();
|
|
}
|
|
|
|
createMessageBox() {
|
|
// Message display area
|
|
this.messageBox = this.add.rectangle(400, 500, 760, 80, 0x000000, 0.8);
|
|
this.messageBox.setStrokeStyle(2, 0xcccccc);
|
|
|
|
this.messageText = this.add.text(400, 500, 'Ahoy, matey! Welcome aboard. Have a look around the vessel.', {
|
|
fontSize: '16px',
|
|
fill: '#fff',
|
|
wordWrap: { width: 740 },
|
|
align: 'center'
|
|
}).setOrigin(0.5);
|
|
}
|
|
|
|
showMessage(message) {
|
|
this.messageText.setText(message);
|
|
}
|
|
|
|
// Helper method to modify inventory (for future use)
|
|
addToInventory(item, amount) {
|
|
if (this.inventory.hasOwnProperty(item)) {
|
|
this.inventory[item] += amount;
|
|
this.updateInventoryDisplay();
|
|
}
|
|
}
|
|
}
|