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;