From 822c586843c59d04e39216fc16959b7770982b6d Mon Sep 17 00:00:00 2001 From: David Kotnik Date: Sat, 27 Dec 2025 23:32:22 +0100 Subject: [PATCH] feat(expansion): implement Phase 3 (Town Restoration) and Phase 4 (Cannabis Textiles) - Added TownSquareScene and linked it with M key transition - Integrated TownRestorationSystem with material costs and inventory - Added locked shop items in NPCShopSystem until buildings are restored - Updated InteractionSystem to handle ruin restoration triggers - Expanded Cannabis farming to yield Hemp Fiber - Added Hemp Clothing crafting recipe and procedural icons - Refactored StatusEffectSystem and NPCShopSystem to global classes --- data/recipes.json | 15 ++++ index.html | 1 + src/entities/Player.js | 61 +++++++++++++++- src/game.js | 2 +- src/scenes/GameScene.js | 18 ++++- src/scenes/TownSquareScene.js | 92 ++++++++++++++++++++++++ src/systems/FarmingSystem.js | 26 +++++-- src/systems/InteractionSystem.js | 40 +++++++++-- src/systems/NPCShopSystem.js | 56 +++++++++++---- src/systems/StatusEffectSystem.js | 101 +++++++++++++++++++++++++++ src/systems/TownRestorationSystem.js | 29 +++++--- src/utils/TextureGenerator.js | 53 +++++++++++++- 12 files changed, 454 insertions(+), 40 deletions(-) create mode 100644 src/scenes/TownSquareScene.js create mode 100644 src/systems/StatusEffectSystem.js diff --git a/data/recipes.json b/data/recipes.json index f9f9baf5b..f53c9fea9 100644 --- a/data/recipes.json +++ b/data/recipes.json @@ -153,6 +153,21 @@ }, "unlocked": false, "craftTime": 2000 + }, + "hemp_clothing": { + "id": "hemp_clothing", + "name": "Hemp Clothing", + "description": "Durable and sustainable clothing made from hemp fiber.", + "category": "materials", + "ingredients": { + "hemp_fiber": 5 + }, + "result": { + "item": "hemp_clothing", + "quantity": 1 + }, + "unlocked": true, + "craftTime": 3000 } }, "categories": [ diff --git a/index.html b/index.html index 269e6e10a..2cb9c66f0 100644 --- a/index.html +++ b/index.html @@ -204,6 +204,7 @@ + diff --git a/src/entities/Player.js b/src/entities/Player.js index cf7912ffb..71a362048 100644 --- a/src/entities/Player.js +++ b/src/entities/Player.js @@ -164,7 +164,8 @@ class Player { right: Phaser.Input.Keyboard.KeyCodes.RIGHT, // Actions space: Phaser.Input.Keyboard.KeyCodes.SPACE, - shift: Phaser.Input.Keyboard.KeyCodes.SHIFT + shift: Phaser.Input.Keyboard.KeyCodes.SHIFT, + r: Phaser.Input.Keyboard.KeyCodes.R }); // Gamepad Events @@ -306,6 +307,11 @@ class Player { if (this.keys.space && Phaser.Input.Keyboard.JustDown(this.keys.space)) { this.handleFarmingAction(); } + + // 🚬 R KEY - Use Item + if (this.keys.r && Phaser.Input.Keyboard.JustDown(this.keys.r)) { + this.useSelectedItem(); + } } updateHeldItem() { @@ -748,4 +754,57 @@ class Player { return false; } + + useSelectedItem() { + const uiScene = this.scene.scene.get('UIScene'); + const invSys = this.scene.inventorySystem; + + if (!uiScene || !invSys) return; + + const selectedIdx = uiScene.selectedSlot; + const slot = invSys.slots[selectedIdx]; + if (!slot || slot.count <= 0) return; + + console.log(`šŸŒ€ Attempting to use item: ${slot.type}`); + + if (slot.type === 'cannabis_buds') { + // SMOKE IT! + if (this.scene.statusEffectSystem) { + const used = this.scene.statusEffectSystem.applyEffect('high'); + if (used) { + invSys.removeItem(slot.type, 1); + // Visual feedback + this.createSmokeParticles(this.gridX, this.gridY); + this.scene.events.emit('show-floating-text', { + x: this.sprite.x, y: this.sprite.y - 60, text: '🚬 *puff puff*', color: '#55ff55' + }); + } + } + } else if (slot.type === 'health_potion' || slot.type === 'medicine') { + // Generic healing + if (this.hp < this.maxHp) { + this.hp = Math.min(this.maxHp, this.hp + 20); + invSys.removeItem(slot.type, 1); + this.scene.events.emit('show-floating-text', { + x: this.sprite.x, y: this.sprite.y - 60, text: 'šŸ’– Recovered!', color: '#ff5555' + }); + } + } + } + + createSmokeParticles(gridX, gridY) { + // Reuse particle logic, or create locally + for (let i = 0; i < 10; i++) { + const smoke = this.scene.add.circle(this.sprite.x, this.sprite.y - 40, 4 + Math.random() * 4, 0xcccccc, 0.6); + this.scene.tweens.add({ + targets: smoke, + x: smoke.x + (Math.random() - 0.5) * 50, + y: smoke.y - 100 - Math.random() * 50, + alpha: 0, + scale: 3, + duration: 2000 + Math.random() * 1000, + onComplete: () => smoke.destroy() + }); + } + } } diff --git a/src/game.js b/src/game.js index 33710c2df..ee96dafce 100644 --- a/src/game.js +++ b/src/game.js @@ -68,7 +68,7 @@ const config = { debug: false } }, - scene: [BootScene, PreloadScene, TiledTestScene, StoryScene, PrologueScene, GameScene, UIScene], + scene: [BootScene, PreloadScene, TiledTestScene, StoryScene, PrologueScene, GameScene, UIScene, TownSquareScene], scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH diff --git a/src/scenes/GameScene.js b/src/scenes/GameScene.js index b6d886622..607787bcc 100644 --- a/src/scenes/GameScene.js +++ b/src/scenes/GameScene.js @@ -1,4 +1,5 @@ // Game Scene - Glavna igralna scena + class GameScene extends Phaser.Scene { constructor() { super({ key: 'GameScene' }); @@ -6,13 +7,14 @@ class GameScene extends Phaser.Scene { this.terrainContainer = null; this.player = null; this.npcs = []; // Array za NPCje + this.statusEffectSystem = null; - // Settings this.settings = { - viewDistance: 'HIGH', // LOW, MEDIUM, HIGH - particles: 'HIGH', // NONE, LOW, HIGH + viewDistance: 'HIGH', + particles: 'HIGH', shadows: true }; + this.townRestorationSystem = null; } async create() { @@ -258,6 +260,7 @@ class GameScene extends Phaser.Scene { // Initialize Farming System this.farmingSystem = new FarmingSystem(this); + this.statusEffectSystem = new StatusEffectSystem(this); console.log('🌾 Farming system initialized!'); // Initialize Build System @@ -759,6 +762,12 @@ class GameScene extends Phaser.Scene { this.craftingUI.toggle(); } }); + + // Add M key to transition to Town Square + this.input.keyboard.on('keydown-M', () => { + console.log('šŸ”„ Transitioning to Town Square...'); + this.scene.start('TownSquareScene'); + }); }); // ======================================================== @@ -774,6 +783,8 @@ class GameScene extends Phaser.Scene { this.lootSystem = new LootSystem(this); this.interactionSystem = new InteractionSystem(this); this.farmingSystem = new FarmingSystem(this); + this.statusEffectSystem = new StatusEffectSystem(this); + this.townRestorationSystem = new TownRestorationSystem(this); this.buildingSystem = new BuildingSystem(this); // this.pathfinding = new Pathfinding(this); // REMOVED: Using PathfindingSystem (Worker) instead this.questSystem = new QuestSystem(this); @@ -1879,6 +1890,7 @@ class GameScene extends Phaser.Scene { if (this.lootSystem) this.lootSystem.update(delta); if (this.interactionSystem) this.interactionSystem.update(delta); if (this.farmingSystem) this.farmingSystem.update(delta); + if (this.statusEffectSystem) this.statusEffectSystem.update(time, delta); if (this.buildSystem) this.buildSystem.update(delta); if (this.questSystem) this.questSystem.update(delta); if (this.multiplayerSystem) this.multiplayerSystem.update(delta); diff --git a/src/scenes/TownSquareScene.js b/src/scenes/TownSquareScene.js new file mode 100644 index 000000000..b481f2449 --- /dev/null +++ b/src/scenes/TownSquareScene.js @@ -0,0 +1,92 @@ +class TownSquareScene extends Phaser.Scene { + constructor() { + super({ key: 'TownSquareScene' }); + this.buildingsSprites = new Map(); + } + + create() { + console.log('šŸ˜ļø TownSquareScene: Initialized!'); + this.cameras.main.setBackgroundColor('#8B4513'); + + // Access the global TownRestorationSystem + this.townRestorationSystem = window.townRestorationSystem || new TownRestorationSystem(this); + + this.add.text(512, 50, 'TOWN SQUARE', { + fontSize: '48px', color: '#FFD700', fontFamily: 'Courier New', fontStyle: 'bold' + }).setOrigin(0.5); + + this.add.text(512, 100, 'Press M to go back to Farm', { + fontSize: '18px', color: '#ffffff', fontFamily: 'Courier New' + }).setOrigin(0.5); + + // Transition back + this.input.keyboard.on('keydown-M', () => { + this.scene.start('GameScene'); + }); + + // Create Ruins + this.createBuilding(400, 300, 'jakob_shop', 'Jakob\'s Shop'); + this.createBuilding(650, 300, 'lena_bakery', 'Lena\'s Bakery'); + this.createBuilding(512, 500, 'dr_chen_clinic', 'Dr. Chen\'s Clinic'); + } + + createBuilding(x, y, id, name) { + if (!this.townRestorationSystem) return; + + const buildingData = this.townRestorationSystem.buildings.get(id); + const isRestored = buildingData ? buildingData.isRestored : false; + + const color = isRestored ? 0x44aa44 : 0x666666; + const base = this.add.rectangle(x, y, 150, 150, color, 0.8); + base.setStrokeStyle(3, 0xffffff); + + const label = this.add.text(x, y, `${name}\n${isRestored ? 'āœ… RESTORED' : 'šŸšļø RUIN'}`, { + fontSize: '16px', color: '#ffffff', align: 'center', fontFamily: 'Courier New', fontStyle: 'bold' + }).setOrigin(0.5); + + base.setInteractive(); + base.on('pointerdown', () => { + const currentData = this.townRestorationSystem.buildings.get(id); + if (!currentData || !currentData.isRestored) { + this.interactWithRuin(id); + } else { + const ui = this.scene.scene.get('UIScene'); + if (ui && ui.showNotification) { + ui.showNotification({ text: `${name} is functional!`, icon: 'šŸŖ' }); + } + } + }); + + this.buildingsSprites.set(id, { base, label, name }); + } + + interactWithRuin(id) { + const building = this.townRestorationSystem.buildings.get(id); + if (!building) return; + + if (this.townRestorationSystem.hasMaterials(building.materials)) { + this.townRestorationSystem.startRestoration(id); + } else { + const ui = this.scene.scene.get('UIScene'); + if (ui && ui.showNotification) { + ui.showNotification({ + title: 'Resources Needed', + text: `Wood: ${building.materials.wood}, Stone: ${building.materials.stone}`, + icon: 'šŸ“¦' + }); + } + } + } + + updateBuildingVisuals(id) { + const sprite = this.buildingsSprites.get(id); + if (sprite) { + sprite.base.setFillStyle(0x44aa44); + sprite.label.setText(`${sprite.name}\nāœ… RESTORED`); + + // Celebration effect + this.cameras.main.shake(500, 0.01); + console.log(`✨ Visuals updated for ${id}`); + } + } +} diff --git a/src/systems/FarmingSystem.js b/src/systems/FarmingSystem.js index f12b2521a..bdd3db872 100644 --- a/src/systems/FarmingSystem.js +++ b/src/systems/FarmingSystem.js @@ -18,6 +18,13 @@ class FarmingSystem { daysPerStage: 2, sellPrice: 15, seedCost: 8 + }, + 'cannabis': { + name: 'Cannabis', + growthStages: 4, + daysPerStage: 2, + sellPrice: 60, + seedCost: 20 } }; } @@ -154,16 +161,18 @@ class FarmingSystem { // Determine texture based on stage let textureKey = `crop_${crop.type}_stage_${crop.stage}`; - // Fallback textures - if (!this.scene.textures.exists(textureKey)) { - textureKey = 'carrots_stages'; // Use carrot stages as fallback - } - if (this.scene.textures.exists(textureKey)) { crop.sprite = this.scene.add.sprite(screenPos.x, screenPos.y, textureKey); crop.sprite.setOrigin(0.5, 1); crop.sprite.setScale(0.8); crop.sprite.setDepth(this.scene.iso.getDepth(crop.gridX, crop.gridY)); + + // Apply tilts/fallback tints + if (textureKey === 'carrots_stages' && crop.type === 'cannabis') { + crop.sprite.setTint(0x55ff55); // Green tint for cannabis fallback + } else if (crop.type === 'cannabis') { + crop.sprite.setTint(0x88ff88); // Subtle green boost for specific texture + } } } @@ -182,7 +191,12 @@ class FarmingSystem { // Give items if (this.scene.inventorySystem) { - this.scene.inventorySystem.addItem(crop.type, 1); + if (crop.type === 'cannabis') { + this.scene.inventorySystem.addItem('cannabis_buds', 2); + this.scene.inventorySystem.addItem('hemp_fiber', 3); + } else { + this.scene.inventorySystem.addItem(crop.type, 1); + } } // Give gold diff --git a/src/systems/InteractionSystem.js b/src/systems/InteractionSystem.js index afea51493..d9a9951b7 100644 --- a/src/systems/InteractionSystem.js +++ b/src/systems/InteractionSystem.js @@ -259,13 +259,10 @@ class InteractionSystem { const decor = this.scene.terrainSystem.decorationsMap.get(decorKey); if (decor && decor.type === 'chest') { - // Open chest and spawn loot! + // ... chest opening logic console.log('šŸ“¦ Opening treasure chest!'); - - // Random loot const lootTable = ['item_scrap', 'item_chip', 'item_wood', 'item_stone', 'item_bone']; const lootCount = Phaser.Math.Between(2, 4); - for (let i = 0; i < lootCount; i++) { const randomLoot = Phaser.Utils.Array.GetRandom(lootTable); if (this.scene.lootSystem) { @@ -274,13 +271,44 @@ class InteractionSystem { this.scene.lootSystem.spawnLoot(gridX + offsetX, gridY + offsetY, randomLoot); } } - - // Remove chest this.scene.terrainSystem.removeDecoration(gridX, gridY); return; } + + // RUIN INTERACTION + if (decor && decor.type.includes('ruin')) { + console.log('šŸšļø Interacted with Ruin at', gridX, gridY); + if (this.scene.townRestorationSystem) { + // Find a building that matches this location or type + // For now, let's just trigger a generic notification or the first building + const building = this.scene.townRestorationSystem.buildings.get('jakob_shop'); // Demo link + if (building) { + this.scene.events.emit('show-floating-text', { + x: gridX * 48, y: gridY * 48 - 40, + text: `Restore ${building.name}?`, + color: '#FFD700' + }); + + // Try start restoration if resources exist + if (this.scene.townRestorationSystem.hasMaterials(building.materials)) { + this.scene.townRestorationSystem.startRestoration(building.id); + } else { + const ui = this.scene.scene.get('UIScene'); + if (ui && ui.showNotification) { + ui.showNotification({ + title: 'Missing Materials', + text: `Need Wood: ${building.materials.wood}, Stone: ${building.materials.stone}`, + icon: 'šŸ“¦' + }); + } + } + } + } + return; + } } + // 4. Try Planting Tree (Manual Planting) if (!isAttack && (activeTool === 'tree_sapling' || activeTool === 'item_sapling')) { const tile = this.scene.terrainSystem.getTile(gridX, gridY); diff --git a/src/systems/NPCShopSystem.js b/src/systems/NPCShopSystem.js index b04c37022..e06897e18 100644 --- a/src/systems/NPCShopSystem.js +++ b/src/systems/NPCShopSystem.js @@ -14,7 +14,7 @@ * @date 2025-12-23 */ -export default class NPCShopSystem { +class NPCShopSystem { constructor(scene) { this.scene = scene; @@ -52,15 +52,16 @@ export default class NPCShopSystem { location: { x: 200, y: 200 }, inventory: [ // Tools - { id: 'iron_axe', name: 'Iron Axe', price: 200, stock: 5, category: 'tools' }, - { id: 'iron_pickaxe', name: 'Iron Pickaxe', price: 200, stock: 5, category: 'tools' }, - { id: 'iron_hoe', name: 'Iron Hoe', price: 150, stock: 5, category: 'tools' }, + { id: 'iron_axe', name: 'Iron Axe', price: 200, stock: 5, category: 'tools', locked: true }, + { id: 'iron_pickaxe', name: 'Iron Pickaxe', price: 200, stock: 5, category: 'tools', locked: true }, + { id: 'iron_hoe', name: 'Iron Hoe', price: 150, stock: 5, category: 'tools', locked: true }, { id: 'watering_can', name: 'Watering Can', price: 100, stock: 10, category: 'tools' }, // Weapons - { id: 'iron_sword', name: 'Iron Sword', price: 500, stock: 3, category: 'weapons' }, - { id: 'steel_sword', name: 'Steel Sword', price: 1000, stock: 2, category: 'weapons' }, - { id: 'crossbow', name: 'Crossbow', price: 800, stock: 2, category: 'weapons' }, + { id: 'iron_sword', name: 'Iron Sword', price: 500, stock: 3, category: 'weapons', locked: true }, + { id: 'steel_sword', name: 'Steel Sword', price: 1000, stock: 2, category: 'weapons', locked: true }, + { id: 'crossbow', name: 'Crossbow', price: 800, stock: 2, category: 'weapons', locked: true }, + // Armor { id: 'leather_armor', name: 'Leather Armor', price: 300, stock: 5, category: 'armor' }, @@ -104,6 +105,7 @@ export default class NPCShopSystem { { id: 'corn_seeds', name: 'Corn Seeds', price: 8, stock: 150, category: 'seeds' }, { id: 'tomato_seeds', name: 'Tomato Seeds', price: 10, stock: 100, category: 'seeds' }, { id: 'strawberry_seeds', name: 'Strawberry Seeds', price: 15, stock: 80, category: 'seeds' }, + { id: 'cannabis_seeds', name: 'Cannabis Seeds', price: 20, stock: 50, category: 'seeds' }, // Materials { id: 'wood', name: 'Wood', price: 10, stock: 500, category: 'materials' }, @@ -138,7 +140,7 @@ export default class NPCShopSystem { { id: 'bandage', name: 'Bandage', price: 15, stock: 100, category: 'medical' }, { id: 'medicine', name: 'Medicine', price: 80, stock: 30, category: 'medical' } ], - buyback: ['herbs', 'mushrooms', 'zombie_samples'] + buyback: ['herbs', 'mushrooms', 'zombie_samples', 'cannabis', 'cannabis_buds'] } ]; @@ -332,13 +334,25 @@ export default class NPCShopSystem { this.itemListContainer.add(priceText); // Buy button - const buyBtn = this.scene.add.rectangle(350, y, 100, 35, 0x228B22); - buyBtn.setStrokeStyle(2, 0x32CD32); + const isLocked = item.locked && !this.isShopRestored(this.currentShop.id); + const buyColor = isLocked ? 0x666666 : 0x228B22; + const buyBtn = this.scene.add.rectangle(350, y, 100, 35, buyColor); + buyBtn.setStrokeStyle(2, isLocked ? 0x999999 : 0x32CD32); buyBtn.setInteractive(); - buyBtn.on('pointerdown', () => this.buyItem(item)); + buyBtn.on('pointerdown', () => { + if (isLocked) { + this.showNotification({ + title: 'Shop Ruined', + text: `Restore the shop to unlock this item!`, + icon: 'šŸšļø' + }); + } else { + this.buyItem(item); + } + }); this.itemListContainer.add(buyBtn); - const buyText = this.scene.add.text(350, y, 'BUY', { + const buyText = this.scene.add.text(350, y, isLocked ? 'LOCKED' : 'BUY', { fontSize: '14px', fontFamily: 'Arial', color: '#ffffff', @@ -349,6 +363,24 @@ export default class NPCShopSystem { }); } + isShopRestored(shopId) { + if (!this.scene.townRestorationSystem) return true; // Fallback if system missing + + // Map shopId to buildingId + const mapping = { + 'blacksmith': 'jakob_shop', // or whichever ID correctly matches TownRestorationSystem + 'baker': 'lena_bakery', + 'healer': 'dr_chen_clinic' + }; + + const buildingId = mapping[shopId]; + if (!buildingId) return true; // General trader is always open + + const building = this.scene.townRestorationSystem.buildings.get(buildingId); + return building ? building.isRestored : false; + } + + /** * Filter by category */ diff --git a/src/systems/StatusEffectSystem.js b/src/systems/StatusEffectSystem.js new file mode 100644 index 000000000..5784e234f --- /dev/null +++ b/src/systems/StatusEffectSystem.js @@ -0,0 +1,101 @@ +/** + * StatusEffectSystem.js + * ==================== + * Manages temporary status effects for the player. + */ + +class StatusEffectSystem { + constructor(scene) { + this.scene = scene; + this.activeEffects = new Map(); + + // Initial state + this.originalSpeed = 0; + + console.log('🌈 StatusEffectSystem initialized'); + } + + applyEffect(type, duration = 15000) { + if (this.activeEffects.has(type)) { + // Refresh duration + const effect = this.activeEffects.get(type); + effect.endTime = Date.now() + duration; + console.log(`✨ Refreshed effect: ${type}`); + return true; + } + + const effect = { + type, + startTime: Date.now(), + endTime: Date.now() + duration + }; + + this.activeEffects.set(type, effect); + this.startEffect(type); + return true; + } + + startEffect(type) { + console.log(`šŸš€ Starting effect: ${type}`); + + if (type === 'high') { + // Visual: Rainbow Tint + const uiScene = this.scene.scene.get('UIScene'); + if (uiScene && uiScene.overlayGraphics) { + this.scene.tweens.addCounter({ + from: 0, + to: 360, + duration: 4000, + repeat: -1, + onUpdate: (tween) => { + if (!this.activeEffects.has('high')) return; + const hull = Phaser.Display.Color.HSVToRGB(tween.getValue() / 360, 0.4, 1); + const alpha = 0.1 + Math.sin(tween.getValue() * 0.05) * 0.05; // Breathing alpha + uiScene.overlayGraphics.clear(); + uiScene.overlayGraphics.fillStyle(hull.color, alpha); + uiScene.overlayGraphics.fillRect(0, 0, this.scene.scale.width, this.scene.scale.height); + } + }); + } + + // Gameplay: Movement slow + if (this.scene.player) { + this.originalSpeed = this.scene.player.moveSpeed; + this.scene.player.moveSpeed *= 0.6; + } + + // Camera Shake/Waves? + this.scene.cameras.main.setLerp(0.05, 0.05); // Looser camera + } + } + + stopEffect(type) { + console.log(`šŸ›‘ Stopping effect: ${type}`); + this.activeEffects.delete(type); + + if (type === 'high') { + // Clear visual + const uiScene = this.scene.scene.get('UIScene'); + if (uiScene && uiScene.overlayGraphics) { + uiScene.overlayGraphics.clear(); + } + + // Reset movement + if (this.scene.player) { + this.scene.player.moveSpeed = this.originalSpeed || 100; + } + + // Reset camera + this.scene.cameras.main.setLerp(1, 1); + } + } + + update(time, delta) { + const now = Date.now(); + this.activeEffects.forEach((effect, type) => { + if (now >= effect.endTime) { + this.stopEffect(type); + } + }); + } +} diff --git a/src/systems/TownRestorationSystem.js b/src/systems/TownRestorationSystem.js index e7c1db2f5..489e05ffb 100644 --- a/src/systems/TownRestorationSystem.js +++ b/src/systems/TownRestorationSystem.js @@ -208,6 +208,11 @@ class TownRestorationSystem { // Check milestones this.checkMilestones(); + // Update visuals in the current scene + if (this.scene.updateBuildingVisuals) { + this.scene.updateBuildingVisuals(buildingId); + } + this.showNotification({ title: 'Building Complete!', text: `āœ… ${building.name} restored!`, @@ -319,19 +324,23 @@ class TownRestorationSystem { return rewards[milestone] || { text: 'Bonus unlocked!' }; } - /** - * Check materials - */ - hasMaterials(required) { - // TODO: Check actual inventory - return true; // For now + hasMaterials(materials) { + if (!this.scene.inventorySystem) return true; // Safety fallback + + for (const [item, count] of Object.entries(materials)) { + if (!this.scene.inventorySystem.hasItem(item, count)) { + return false; + } + } + return true; } - /** - * Consume materials - */ consumeMaterials(materials) { - // TODO: Remove from inventory + if (!this.scene.inventorySystem) return; + + for (const [item, count] of Object.entries(materials)) { + this.scene.inventorySystem.removeItem(item, count); + } console.log('šŸ“¦ Materials consumed:', materials); } diff --git a/src/utils/TextureGenerator.js b/src/utils/TextureGenerator.js index 6fd1ca51a..5169bc867 100644 --- a/src/utils/TextureGenerator.js +++ b/src/utils/TextureGenerator.js @@ -713,7 +713,11 @@ class TextureGenerator { { name: 'artefact_old', color: '#8B4513' }, // Ancient Pot (Brown) { name: 'milk', color: '#FFFFFF' }, // White { name: 'glowing_milk', color: '#00FF00' }, // Green - { name: 'animal_feed', color: '#F4A460' } // Sandy Brown (Feed) + { name: 'animal_feed', color: '#F4A460' }, // Sandy Brown (Feed) + { name: 'cannabis', color: '#228B22' }, // Green + { name: 'cannabis_seeds', color: '#55ff55' }, // Lighter Green + { name: 'hemp_fiber', color: '#DEB887' }, // Tan/Beige + { name: 'hemp_clothing', color: '#2E8B57' } // SeaGreen ]; items.forEach(item => { const it = typeof item === 'string' ? item : item.name; @@ -747,6 +751,17 @@ class TextureGenerator { x.fillStyle = '#DAA520'; x.fillRect(13, 22, 6, 3); } + else if (it === 'cannabis') { + // Cannabis Leaf (Stylized) + x.fillStyle = '#228B22'; + // Center leaf + x.beginPath(); x.ellipse(16, 12, 3, 10, 0, 0, Math.PI * 2); x.fill(); + // Side leaves + x.beginPath(); x.ellipse(10, 16, 3, 8, -Math.PI / 4, 0, Math.PI * 2); x.fill(); + x.beginPath(); x.ellipse(22, 16, 3, 8, Math.PI / 4, 0, Math.PI * 2); x.fill(); + x.beginPath(); x.ellipse(10, 24, 3, 6, -Math.PI / 2.5, 0, Math.PI * 2); x.fill(); + x.beginPath(); x.ellipse(22, 24, 3, 6, Math.PI / 2.5, 0, Math.PI * 2); x.fill(); + } else if (it.includes('seeds')) { // Seed Bag x.fillStyle = '#DEB887'; // Burlywood bag @@ -758,6 +773,42 @@ class TextureGenerator { x.fillStyle = color; x.beginPath(); x.arc(16, 18, 4, 0, Math.PI * 2); x.fill(); } + else if (it === 'cannabis_buds') { + // Stylized "bud" (forest green clusters) + x.fillStyle = '#228B22'; + for (let i = 0; i < 5; i++) { + const ang = (i / 5) * Math.PI * 2; + x.beginPath(); + x.arc(16 + Math.cos(ang) * 6, 16 + Math.sin(ang) * 6, 5, 0, Math.PI * 2); + x.fill(); + } + x.fillStyle = '#8FBC8F'; // Frosted look + x.beginPath(); x.arc(16, 16, 4, 0, Math.PI * 2); x.fill(); + } + else if (it === 'hemp_fiber') { + // Bundle of fibers (tan/straw color) + x.strokeStyle = '#DEB887'; + x.lineWidth = 2; + for (let i = 0; i < 5; i++) { + x.beginPath(); + x.moveTo(10 + i * 3, 24); + x.quadraticCurveTo(16, 16, 10 + (4 - i) * 3, 8); + x.stroke(); + } + // Tie + x.fillStyle = '#8B4513'; + x.fillRect(10, 15, 12, 2); + } + else if (it === 'hemp_clothing') { + // Simple T-shirt shape + x.fillStyle = '#2E8B57'; // SeaGreen + x.fillRect(8, 10, 16, 18); // Body + x.fillRect(4, 10, 6, 8); // Left sleeve + x.fillRect(22, 10, 6, 8); // Right sleeve + // Collar + x.fillStyle = '#000'; + x.beginPath(); x.arc(16, 10, 4, 0, Math.PI); x.fill(); + } else { // Default Circle x.fillStyle = color;