Phase 37: Micro Farm & Expansion System Complete
MICRO FARM SYSTEM (8x8): - Initial 8x8 farm boundary (center spawn) - White boundary visualization - Corner markers for clear boundaries - Locked tile tracking (Set-based) VISUAL FEEDBACK: - Locked tile overlay (30% black) - Clear visual distinction (farm vs locked) - Dynamic rendering (15 tile radius) - Depth-sorted overlays FARMING RESTRICTIONS: - Block tilling outside farm boundary - Error messages (floating text) - Farm boundary validation - FarmingSystem integration EXPANSION SYSTEM: - 4-direction unlock buttons (N/S/E/W) - Cost system (50 gold per expansion) - 2x2 tile unlock increments - Visual updates (boundaries + overlay) UI INTEGRATION: - Interactive expansion buttons - Hover effects (color + scale) - Cost labels (gold display) - Success/error feedback MINIMAP INTEGRATION: - Farm boundary in minimap - White box indicator - Player-relative rendering - Fixed terrain system compatibility TECHNICAL FIXES: - Added decorationsMap to Flat2DTerrainSystem - Fixed variable scope issues - UIScene minimap compatibility - TerrainSystem.getTile() integration FILES CREATED/MODIFIED: - src/systems/MicroFarmSystem.js (NEW!) - src/systems/FarmingSystem.js - src/systems/Flat2DTerrainSystem.js - src/scenes/GameScene.js - src/scenes/UIScene.js - index.html Session: 1h (00:50-01:19) Date: 15.12.2024 Status: PHASE 37 COMPLETE!
This commit is contained in:
@@ -26,6 +26,22 @@ class FarmingSystem {
|
||||
tillSoil(gridX, gridY) {
|
||||
if (!this.scene.terrainSystem) return false;
|
||||
|
||||
// 🌱 CHECK MICRO FARM BOUNDARY!
|
||||
if (this.scene.microFarmSystem && !this.scene.microFarmSystem.isTileUnlocked(gridX, gridY)) {
|
||||
console.log('❌ Cannot till outside farm boundary!');
|
||||
|
||||
// Show error message
|
||||
if (this.scene.events) {
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: gridX * 48,
|
||||
y: gridY * 48,
|
||||
text: '🚫 Unlock farm first!',
|
||||
color: '#ff0000'
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if already tilled
|
||||
const key = `${gridX},${gridY}`;
|
||||
if (this.isTilled(gridX, gridY)) {
|
||||
|
||||
@@ -16,6 +16,9 @@ class Flat2DTerrainSystem {
|
||||
this.pathsLayer = null;
|
||||
this.decorLayer = null;
|
||||
|
||||
// Decoration tracking (for interaction system)
|
||||
this.decorationsMap = new Map();
|
||||
|
||||
// Textures ready flag
|
||||
this.texturesReady = false;
|
||||
|
||||
|
||||
331
src/systems/MicroFarmSystem.js
Normal file
331
src/systems/MicroFarmSystem.js
Normal file
@@ -0,0 +1,331 @@
|
||||
// 🌱 MICRO FARM SYSTEM - Phase 37
|
||||
// Začetna 8x8 parcela z postopno širitvijo
|
||||
|
||||
class MicroFarmSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
// MICRO FARM CONFIG
|
||||
this.farmCenterX = 50; // Center of 100x100 map
|
||||
this.farmCenterY = 50;
|
||||
this.farmSize = 8; // 8x8 tiles (initial)
|
||||
|
||||
// EXPANSION SYSTEM
|
||||
this.unlockedTiles = new Set(); // Tracks unlocked tiles
|
||||
this.expansionCost = 50; // Gold per 2x2 expansion
|
||||
|
||||
// LAND TYPES
|
||||
this.landTypes = {
|
||||
GRASS: 'grass', // Free to use
|
||||
FOREST: 'forest', // Needs clearing (trees)
|
||||
ROCKY: 'rocky', // Needs mining (rocks)
|
||||
SWAMP: 'swamp' // Needs drainage (water)
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log('🌱 MicroFarmSystem initialized');
|
||||
|
||||
// Unlock initial 8x8 farm
|
||||
this.unlockInitialFarm();
|
||||
|
||||
// Create visual boundaries
|
||||
this.createFarmBoundaries();
|
||||
|
||||
// Render locked tile overlay
|
||||
this.renderLockedTileOverlay();
|
||||
|
||||
// Create expansion UI buttons
|
||||
this.createExpansionUI();
|
||||
}
|
||||
|
||||
createExpansionUI() {
|
||||
// Create UI buttons for farm expansion
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) {
|
||||
console.warn('⚠️ UIScene not found - cannot create expansion UI');
|
||||
return;
|
||||
}
|
||||
|
||||
// Store reference
|
||||
this.uiScene = uiScene;
|
||||
this.expansionButtons = [];
|
||||
|
||||
const buttonSize = 40;
|
||||
const buttonColor = 0x8B4513; // Brown
|
||||
const buttonHoverColor = 0xD2691E;
|
||||
const expandCost = this.expansionCost;
|
||||
|
||||
// Button positions relative to farm center
|
||||
const farmWorldX = this.farmCenterX * 48;
|
||||
const farmWorldY = this.farmCenterY * 48;
|
||||
const farmPixelSize = this.farmSize * 48;
|
||||
const halfSize = farmPixelSize / 2;
|
||||
|
||||
const buttons = [
|
||||
{ dir: 'north', x: farmWorldX, y: farmWorldY - halfSize - 60, icon: '⬆️' },
|
||||
{ dir: 'south', x: farmWorldX, y: farmWorldY + halfSize + 60, icon: '⬇️' },
|
||||
{ dir: 'east', x: farmWorldX + halfSize + 60, y: farmWorldY, icon: '➡️' },
|
||||
{ dir: 'west', x: farmWorldX - halfSize - 60, y: farmWorldY, icon: '⬅️' }
|
||||
];
|
||||
|
||||
buttons.forEach(btn => {
|
||||
// Button background
|
||||
const bg = this.scene.add.rectangle(btn.x, btn.y, buttonSize, buttonSize, buttonColor);
|
||||
bg.setStrokeStyle(2, 0xFFFFFF);
|
||||
bg.setDepth(10);
|
||||
bg.setInteractive({ useHandCursor: true });
|
||||
|
||||
// Button text
|
||||
const text = this.scene.add.text(btn.x, btn.y, btn.icon, {
|
||||
fontSize: '20px',
|
||||
color: '#ffffff'
|
||||
}).setOrigin(0.5);
|
||||
text.setDepth(11);
|
||||
|
||||
// Cost label
|
||||
const costLabel = this.scene.add.text(btn.x, btn.y + 30, `${expandCost}g`, {
|
||||
fontSize: '12px',
|
||||
color: '#FFD700',
|
||||
fontStyle: 'bold'
|
||||
}).setOrigin(0.5);
|
||||
costLabel.setDepth(11);
|
||||
|
||||
// Hover effects
|
||||
bg.on('pointerover', () => {
|
||||
bg.setFillStyle(buttonHoverColor);
|
||||
bg.setScale(1.1);
|
||||
});
|
||||
|
||||
bg.on('pointerout', () => {
|
||||
bg.setFillStyle(buttonColor);
|
||||
bg.setScale(1.0);
|
||||
});
|
||||
|
||||
// Click handler
|
||||
bg.on('pointerdown', () => {
|
||||
this.tryExpandFarm(btn.dir);
|
||||
});
|
||||
|
||||
this.expansionButtons.push({ bg, text, costLabel, dir: btn.dir });
|
||||
});
|
||||
|
||||
console.log('✅ Expansion UI created!');
|
||||
}
|
||||
|
||||
tryExpandFarm(direction) {
|
||||
// Check if player has enough gold
|
||||
const inv = this.scene.inventorySystem;
|
||||
if (!inv || inv.gold < this.expansionCost) {
|
||||
console.log(`❌ Not enough gold! Need ${this.expansionCost}g`);
|
||||
|
||||
if (this.scene.events) {
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: this.scene.cameras.main.width / 2,
|
||||
y: 100,
|
||||
text: `Need ${this.expansionCost} gold!`,
|
||||
color: '#ff0000'
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduct gold
|
||||
inv.gold -= this.expansionCost;
|
||||
|
||||
// Expand farm
|
||||
this.expandFarm(direction);
|
||||
|
||||
// Success feedback
|
||||
if (this.scene.events) {
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: this.scene.cameras.main.width / 2,
|
||||
y: 100,
|
||||
text: `✅ Farm expanded ${direction.toUpperCase()}!`,
|
||||
color: '#00ff00'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ Farm expanded ${direction}! (-${this.expansionCost}g)`);
|
||||
}
|
||||
|
||||
unlockInitialFarm() {
|
||||
// Unlock central 8x8 tiles
|
||||
const halfSize = Math.floor(this.farmSize / 2);
|
||||
|
||||
for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
|
||||
for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
|
||||
const tileKey = `${x},${y}`;
|
||||
this.unlockedTiles.add(tileKey);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Unlocked ${this.unlockedTiles.size} tiles (8x8 micro farm)`);
|
||||
}
|
||||
|
||||
createFarmBoundaries() {
|
||||
// Visual indicator of farm boundaries
|
||||
const graphics = this.scene.add.graphics();
|
||||
const halfSize = Math.floor(this.farmSize / 2);
|
||||
|
||||
const startX = (this.farmCenterX - halfSize) * 48;
|
||||
const startY = (this.farmCenterY - halfSize) * 48;
|
||||
const width = this.farmSize * 48;
|
||||
const height = this.farmSize * 48;
|
||||
|
||||
// Farm border (white dashed line)
|
||||
graphics.lineStyle(3, 0xFFFFFF, 0.8);
|
||||
graphics.strokeRect(startX, startY, width, height);
|
||||
|
||||
// Corner markers
|
||||
graphics.fillStyle(0xFFFFFF, 0.9);
|
||||
const markerSize = 8;
|
||||
graphics.fillRect(startX - markerSize / 2, startY - markerSize / 2, markerSize, markerSize);
|
||||
graphics.fillRect(startX + width - markerSize / 2, startY - markerSize / 2, markerSize, markerSize);
|
||||
graphics.fillRect(startX - markerSize / 2, startY + height - markerSize / 2, markerSize, markerSize);
|
||||
graphics.fillRect(startX + width - markerSize / 2, startY + height - markerSize / 2, markerSize, markerSize);
|
||||
|
||||
graphics.setDepth(5); // Above ground, below player
|
||||
|
||||
// Store graphics for later updates
|
||||
this.boundaryGraphics = graphics;
|
||||
}
|
||||
|
||||
renderLockedTileOverlay() {
|
||||
// Render dark overlay on locked tiles
|
||||
if (!this.scene || !this.lockedOverlayGraphics) {
|
||||
// Create overlay graphics if not exists
|
||||
this.lockedOverlayGraphics = this.scene.add.graphics();
|
||||
this.lockedOverlayGraphics.setDepth(4); // Above ground, below boundaries
|
||||
}
|
||||
|
||||
this.lockedOverlayGraphics.clear();
|
||||
|
||||
// Darken all tiles that are NOT unlocked
|
||||
const halfSize = Math.floor(this.farmSize / 2);
|
||||
const farmStartX = this.farmCenterX - halfSize;
|
||||
const farmEndX = this.farmCenterX + halfSize;
|
||||
const farmStartY = this.farmCenterY - halfSize;
|
||||
const farmEndY = this.farmCenterY + halfSize;
|
||||
|
||||
// Render grid of locked tiles
|
||||
const viewRange = 15; // Show some area around farm
|
||||
for (let y = this.farmCenterY - viewRange; y < this.farmCenterY + viewRange; y++) {
|
||||
for (let x = this.farmCenterX - viewRange; x < this.farmCenterX + viewRange; x++) {
|
||||
// Skip if within farm boundaries
|
||||
if (x >= farmStartX && x < farmEndX && y >= farmStartY && y < farmEndY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw dark overlay (lighter)
|
||||
const worldX = x * 48;
|
||||
const worldY = y * 48;
|
||||
this.lockedOverlayGraphics.fillStyle(0x000000, 0.3); // 0.5 -> 0.3
|
||||
this.lockedOverlayGraphics.fillRect(worldX, worldY, 48, 48);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🔒 Locked tile overlay rendered!');
|
||||
}
|
||||
|
||||
isTileUnlocked(tileX, tileY) {
|
||||
const tileKey = `${tileX},${tileY}`;
|
||||
return this.unlockedTiles.has(tileKey);
|
||||
}
|
||||
|
||||
canExpand(direction) {
|
||||
// Check if expansion in direction is possible
|
||||
// direction: 'north', 'south', 'east', 'west'
|
||||
// TODO: Implement expansion logic
|
||||
return true;
|
||||
}
|
||||
|
||||
expandFarm(direction) {
|
||||
// Unlock 2x2 tiles in specified direction
|
||||
const halfSize = Math.floor(this.farmSize / 2);
|
||||
const expansionSize = 2; // Unlock 2x2 tiles at a time
|
||||
|
||||
let newTiles = [];
|
||||
|
||||
switch (direction) {
|
||||
case 'north':
|
||||
for (let y = this.farmCenterY - halfSize - expansionSize; y < this.farmCenterY - halfSize; y++) {
|
||||
for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
|
||||
newTiles.push({ x, y });
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'south':
|
||||
for (let y = this.farmCenterY + halfSize; y < this.farmCenterY + halfSize + expansionSize; y++) {
|
||||
for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
|
||||
newTiles.push({ x, y });
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'east':
|
||||
for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
|
||||
for (let x = this.farmCenterX + halfSize; x < this.farmCenterX + halfSize + expansionSize; x++) {
|
||||
newTiles.push({ x, y });
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'west':
|
||||
for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
|
||||
for (let x = this.farmCenterX - halfSize - expansionSize; x < this.farmCenterX - halfSize; x++) {
|
||||
newTiles.push({ x, y });
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Unlock the tiles
|
||||
newTiles.forEach(tile => {
|
||||
const tileKey = `${tile.x},${tile.y}`;
|
||||
this.unlockedTiles.add(tileKey);
|
||||
});
|
||||
|
||||
// Update farm size
|
||||
if (direction === 'north' || direction === 'south') {
|
||||
this.farmSize += expansionSize;
|
||||
} else {
|
||||
this.farmSize += expansionSize;
|
||||
}
|
||||
|
||||
// Update visuals
|
||||
this.createFarmBoundaries(); // Redraw boundaries
|
||||
this.renderLockedTileOverlay(); // Update overlay
|
||||
|
||||
console.log(`🔓 Expanded farm ${direction}! +${newTiles.length} tiles. Total: ${this.unlockedTiles.size}`);
|
||||
}
|
||||
|
||||
getLandType(tileX, tileY) {
|
||||
// Determine land type based on tile position
|
||||
// TODO: Use terrain system data
|
||||
|
||||
// For now, return grass for unlocked tiles
|
||||
if (this.isTileUnlocked(tileX, tileY)) {
|
||||
return this.landTypes.GRASS;
|
||||
}
|
||||
|
||||
// Surrounding areas have different types
|
||||
const distFromCenter = Math.sqrt(
|
||||
Math.pow(tileX - this.farmCenterX, 2) +
|
||||
Math.pow(tileY - this.farmCenterY, 2)
|
||||
);
|
||||
|
||||
if (distFromCenter < 10) return this.landTypes.GRASS;
|
||||
if (distFromCenter < 15) return this.landTypes.FOREST;
|
||||
if (distFromCenter < 20) return this.landTypes.ROCKY;
|
||||
return this.landTypes.SWAMP;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// Cleanup
|
||||
this.unlockedTiles.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user