/** * 📦 RESOURCE LOGISTICS SYSTEM - Week 1 Priority * Auto-pickup resources + Storage management + Resource tracking * * Features: * - Auto-pickup dropped resources * - Storage capacity management * - Resource depot system * - Visual feedback (UI icons + VFX) * - Integration with Zombie Hauler workers * * Assets Used: * - /assets/ui/resource_icon_wood.png * - /assets/ui/resource_icon_food.png * - /assets/ui/resource_icon_stone.png * - /assets/buildings/resource_depot.png * - /assets/buildings/resource_pile_wood.png * - /assets/buildings/resource_pile_food.png * - /assets/vfx/resource_collect.png */ export default class ResourceLogisticsSystem { constructor(scene) { this.scene = scene; // Resource tracking this.resources = { wood: 0, food: 0, stone: 0, coal: 0 }; // Storage system this.storageCapacity = { wood: 100, food: 100, stone: 100, coal: 50 }; // Auto-pickup settings this.autoPickupEnabled = true; this.pickupRadius = 80; // pixels this.pickupDelay = 500; // ms between pickups this.lastPickupTime = 0; // Depot locations (can have multiple) this.depots = []; // Dropped resources on map this.droppedResources = []; // UI elements this.resourceUI = null; this.init(); } init() { console.log('[ResourceLogistics] Initializing resource logistics system...'); // Create resource UI this.createResourceUI(); // Load resource sprites this.loadResourceSprites(); // Setup auto-pickup update loop this.scene.time.addEvent({ delay: this.pickupDelay, callback: () => this.updateAutoPickup(), loop: true }); } loadResourceSprites() { // Preload resource icons if (!this.scene.textures.exists('resource_icon_wood')) { this.scene.load.image('resource_icon_wood', 'assets/ui/resource_icon_wood.png'); } if (!this.scene.textures.exists('resource_icon_food')) { this.scene.load.image('resource_icon_food', 'assets/ui/resource_icon_food.png'); } if (!this.scene.textures.exists('resource_icon_stone')) { this.scene.load.image('resource_icon_stone', 'assets/ui/resource_icon_stone.png'); } if (!this.scene.textures.exists('resource_collect_vfx')) { this.scene.load.image('resource_collect_vfx', 'assets/vfx/resource_collect.png'); } this.scene.load.start(); } createResourceUI() { const padding = 20; const iconSize = 32; const spacing = 100; this.resourceUI = this.scene.add.container(padding, padding); this.resourceUI.setScrollFactor(0); this.resourceUI.setDepth(1000); // Wood display this.woodIcon = this.scene.add.sprite(0, 0, 'resource_icon_wood').setOrigin(0, 0); this.woodText = this.scene.add.text(iconSize + 10, iconSize / 2, '0/100', { fontSize: '18px', fill: '#fff', stroke: '#000', strokeThickness: 3 }).setOrigin(0, 0.5); // Food display this.foodIcon = this.scene.add.sprite(spacing, 0, 'resource_icon_food').setOrigin(0, 0); this.foodText = this.scene.add.text(spacing + iconSize + 10, iconSize / 2, '0/100', { fontSize: '18px', fill: '#fff', stroke: '#000', strokeThickness: 3 }).setOrigin(0, 0.5); // Stone display this.stoneIcon = this.scene.add.sprite(spacing * 2, 0, 'resource_icon_stone').setOrigin(0, 0); this.stoneText = this.scene.add.text(spacing * 2 + iconSize + 10, iconSize / 2, '0/100', { fontSize: '18px', fill: '#fff', stroke: '#000', strokeThickness: 3 }).setOrigin(0, 0.5); this.resourceUI.add([ this.woodIcon, this.woodText, this.foodIcon, this.foodText, this.stoneIcon, this.stoneText ]); } updateResourceUI() { if (this.woodText) { this.woodText.setText(`${this.resources.wood}/${this.storageCapacity.wood}`); } if (this.foodText) { this.foodText.setText(`${this.resources.food}/${this.storageCapacity.food}`); } if (this.stoneText) { this.stoneText.setText(`${this.resources.stone}/${this.storageCapacity.stone}`); } } /** * Auto-pickup resources near player */ updateAutoPickup() { if (!this.autoPickupEnabled) return; if (!this.scene.player) return; const currentTime = Date.now(); if (currentTime - this.lastPickupTime < this.pickupDelay) return; const playerX = this.scene.player.x; const playerY = this.scene.player.y; // Check each dropped resource for (let i = this.droppedResources.length - 1; i >= 0; i--) { const resource = this.droppedResources[i]; // Calculate distance const distance = Phaser.Math.Distance.Between( playerX, playerY, resource.x, resource.y ); // Auto-pickup if within radius if (distance <= this.pickupRadius) { this.pickupResource(resource, i); this.lastPickupTime = currentTime; break; // One at a time for smooth feel } } } /** * Pickup a resource */ pickupResource(resource, index) { const type = resource.type; const amount = resource.amount; // Check storage capacity if (this.resources[type] >= this.storageCapacity[type]) { // Storage full! this.showStorageFullMessage(type); return; } // Add to resources const added = Math.min(amount, this.storageCapacity[type] - this.resources[type]); this.resources[type] += added; // Play collection VFX this.playCollectionVFX(resource.x, resource.y); // Play collection sound this.playCollectionSound(type); // Remove from world if (resource.sprite) { resource.sprite.destroy(); } this.droppedResources.splice(index, 1); // Update UI this.updateResourceUI(); console.log(`[ResourceLogistics] Picked up ${added} ${type}! Total: ${this.resources[type]}/${this.storageCapacity[type]}`); } /** * Drop resource on map (from tree, mining, etc.) */ dropResource(x, y, type, amount) { // Create visual dropped resource const sprite = this.scene.add.sprite(x, y, `resource_pile_${type}`); sprite.setScale(0.5); // Add to tracking this.droppedResources.push({ x: x, y: y, type: type, amount: amount, sprite: sprite }); // Bounce animation this.scene.tweens.add({ targets: sprite, y: y - 10, duration: 200, yoyo: true, ease: 'Quad.easeOut' }); } /** * Play collection VFX (green/gold sparkles) */ playCollectionVFX(x, y) { if (!this.scene.textures.exists('resource_collect_vfx')) return; // Create particle emitter for collection effect const emitter = this.scene.add.particles(x, y, 'resource_collect_vfx', { speed: { min: 50, max: 100 }, angle: { min: 0, max: 360 }, scale: { start: 1, end: 0 }, alpha: { start: 1, end: 0 }, lifespan: 500, quantity: 8, blendMode: 'ADD' }); // Destroy after animation this.scene.time.delayedCall(600, () => { emitter.destroy(); }); } /** * Play collection sound */ playCollectionSound(type) { // Use existing sound system if available if (this.scene.soundManager) { this.scene.soundManager.playSound('resource_pickup'); } } /** * Show storage full message */ showStorageFullMessage(type) { const message = `${type.toUpperCase()} STORAGE FULL! Build more storage or upgrade depot.`; // Use existing popup system if available if (this.scene.centralPopupSystem) { this.scene.centralPopupSystem.showMessage(message, 'warning'); } else { console.warn(`[ResourceLogistics] ${message}`); } } /** * Add a resource depot */ addDepot(x, y, capacity = {}) { const depot = { x: x, y: y, sprite: this.scene.add.sprite(x, y, 'resource_depot'), capacity: { wood: capacity.wood || 100, food: capacity.food || 100, stone: capacity.stone || 100, coal: capacity.coal || 50 } }; this.depots.push(depot); // Increase storage capacity this.storageCapacity.wood += depot.capacity.wood; this.storageCapacity.food += depot.capacity.food; this.storageCapacity.stone += depot.capacity.stone; this.storageCapacity.coal += depot.capacity.coal; this.updateResourceUI(); console.log('[ResourceLogistics] Depot added! New capacity:', this.storageCapacity); } /** * Get current resources */ getResources() { return { ...this.resources }; } /** * Add resources (for debugging or harvesting) */ addResource(type, amount) { const added = Math.min(amount, this.storageCapacity[type] - this.resources[type]); this.resources[type] += added; this.updateResourceUI(); return added; } /** * Remove resources (for building, crafting, etc.) */ removeResource(type, amount) { if (this.resources[type] >= amount) { this.resources[type] -= amount; this.updateResourceUI(); return true; } return false; } /** * Check if has enough resources */ hasResources(requirements) { for (const [type, amount] of Object.entries(requirements)) { if (this.resources[type] < amount) { return false; } } return true; } update(time, delta) { // Already handled by time events } destroy() { if (this.resourceUI) { this.resourceUI.destroy(); } // Clear dropped resources this.droppedResources.forEach(resource => { if (resource.sprite) { resource.sprite.destroy(); } }); this.droppedResources = []; // Clear depots this.depots.forEach(depot => { if (depot.sprite) { depot.sprite.destroy(); } }); this.depots = []; } }