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:
2025-12-15 01:20:14 +01:00
parent 344fbc307d
commit 4cf6350d11
6 changed files with 400 additions and 22 deletions

View File

@@ -84,6 +84,11 @@ class GameScene extends Phaser.Scene {
this.buildSystem = new BuildSystem(this);
console.log('🏗️ Build system initialized!');
// 🌱 Initialize Micro Farm System (PHASE 37!)
this.microFarmSystem = new MicroFarmSystem(this);
console.log('🌱 Micro Farm system initialized!');
// ========================================================
// 🏗️ TESTNI PRIMERI - Postavitev Ograj (ONEMOGOČENO)
// ========================================================

View File

@@ -2689,7 +2689,7 @@ class UIScene extends Phaser.Scene {
*/
updateMinimapContent() {
if (!this.minimapExpanded) return;
if (!this.gameScene || !this.gameScene.terrain) return;
if (!this.gameScene) return;
this.minimapGraphics.clear();
@@ -2698,38 +2698,60 @@ class UIScene extends Phaser.Scene {
const scale = (mapSize * 2) / worldSize;
// Get player position
const playerPos = this.gameScene.player.getPosition();
const centerX = playerPos.x;
const centerY = playerPos.y;
const player = this.gameScene.player;
if (!player) return;
const centerX = Math.floor(player.gridX || 0);
const centerY = Math.floor(player.gridY || 0);
// View radius around player
const viewRadius = 25;
// Draw terrain tiles
for (let y = Math.max(0, centerY - viewRadius); y < Math.min(worldSize, centerY + viewRadius); y++) {
for (let x = Math.max(0, centerX - viewRadius); x < Math.min(worldSize, centerX + viewRadius); x++) {
const tile = this.gameScene.terrain[y] && this.gameScene.terrain[y][x];
if (!tile) continue;
if (this.gameScene.terrainSystem && this.gameScene.terrainSystem.tiles) {
for (let y = Math.max(0, centerY - viewRadius); y < Math.min(worldSize, centerY + viewRadius); y++) {
for (let x = Math.max(0, centerX - viewRadius); x < Math.min(worldSize, centerX + viewRadius); x++) {
const tile = this.gameScene.terrainSystem.getTile(x, y);
if (!tile) continue;
// Calculate position relative to player
const relX = (x - centerX) * scale;
const relY = (y - centerY) * scale;
// Calculate position relative to player
const relX = (x - centerX) * scale;
const relY = (y - centerY) * scale;
// Only draw if within circle
const dist = Math.sqrt(relX * relX + relY * relY);
if (dist > mapSize) continue;
// Only draw if within circle
const dist = Math.sqrt(relX * relX + relY * relY);
if (dist > mapSize) continue;
// Color based on tile type
let color = 0x228B22; // Default green
if (tile.type === 'water') color = 0x4488ff;
else if (tile.type === 'sand') color = 0xf4a460;
else if (tile.type === 'stone') color = 0x808080;
else if (tile.type === 'grass') color = 0x228B22;
// Color based on tile type
let color = 0x228B22; // Default green
if (tile.type === 'water') color = 0x4488ff;
else if (tile.type === 'sand') color = 0xf4a460;
else if (tile.type === 'stone') color = 0x808080;
else if (tile.type === 'grass') color = 0x228B22;
this.minimapGraphics.fillStyle(color, 0.8);
this.minimapGraphics.fillRect(relX - scale / 2, relY - scale / 2, scale, scale);
this.minimapGraphics.fillStyle(color, 0.8);
this.minimapGraphics.fillRect(relX - scale / 2, relY - scale / 2, scale, scale);
}
}
}
// 🌱 DRAW MICRO FARM BOUNDARY!
if (this.gameScene.microFarmSystem) {
const farmCenterX = this.gameScene.microFarmSystem.farmCenterX;
const farmCenterY = this.gameScene.microFarmSystem.farmCenterY;
const farmSize = this.gameScene.microFarmSystem.farmSize;
const halfSize = Math.floor(farmSize / 2);
// Farm boundary in minimap coords
const farmX1 = (farmCenterX - halfSize - centerX) * scale;
const farmY1 = (farmCenterY - halfSize - centerY) * scale;
const farmW = farmSize * scale;
const farmH = farmSize * scale;
// Draw white farm boundary
this.minimapGraphics.lineStyle(2, 0xFFFFFF, 0.9);
this.minimapGraphics.strokeRect(farmX1, farmY1, farmW, farmH);
}
}
/**

View File

@@ -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)) {

View File

@@ -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;

View 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();
}
}