diff --git a/assets/fonts/EraserDust.ttf b/assets/fonts/EraserDust.ttf new file mode 100644 index 0000000..f17d31f Binary files /dev/null and b/assets/fonts/EraserDust.ttf differ diff --git a/assets/fonts/EraserRegular.ttf b/assets/fonts/EraserRegular.ttf new file mode 100644 index 0000000..ec9f3db Binary files /dev/null and b/assets/fonts/EraserRegular.ttf differ diff --git a/assets/images/backpack.png b/assets/images/backpack.png new file mode 100644 index 0000000..7f092d2 Binary files /dev/null and b/assets/images/backpack.png differ diff --git a/assets/images/objects.png b/assets/images/objects.png index a4f45fe..05f7d4b 100644 Binary files a/assets/images/objects.png and b/assets/images/objects.png differ diff --git a/raw/eraser-dust.zip b/raw/eraser-dust.zip new file mode 100644 index 0000000..fd13777 Binary files /dev/null and b/raw/eraser-dust.zip differ diff --git a/raw/objects.psd b/raw/objects.psd index addc6fd..7b69c0b 100644 Binary files a/raw/objects.psd and b/raw/objects.psd differ diff --git a/src/cycle.js b/src/cycle.js index c8a50af..f8eda3f 100644 --- a/src/cycle.js +++ b/src/cycle.js @@ -30,8 +30,12 @@ export class CycleManager { this.cycleText = this.scene.add.text( 40, 40, - 'Day', - { fontSize: '24px', fill: '#ffffff' } + 'Time of Day: Day', + { + fontSize: '36px', + fill: '#ffffff', + fontFamily: 'eraserDust, arial' + } ).setShadow(3,3, '#333', 5).setScrollFactor(0); } @@ -53,7 +57,7 @@ export class CycleManager { // Update display text if (this.cycleText) { - this.cycleText.setText(this.currentCycle.charAt(0).toUpperCase() + this.currentCycle.slice(1)); + this.cycleText.setText('Time of Day: ' + this.currentCycle.charAt(0).toUpperCase() + this.currentCycle.slice(1)); } // Apply tint to the terrain layer diff --git a/src/inventory.js b/src/inventory.js new file mode 100644 index 0000000..fb9ffe1 --- /dev/null +++ b/src/inventory.js @@ -0,0 +1,126 @@ +export class InventoryManager { + constructor(scene) { + this.scene = scene; + this.isOpen = false; + this.inventory = {}; + this.backpackIcon = null; + this.inventoryUI = null; + this.closeButton = null; + } + + init() { + const cam = this.scene.cameras.main; + this.backpackIcon = this.scene.physics.add.image(cam.worldView.width-150, cam.worldView.height-150, 'backpack') + .setOrigin(0.5) + .setScale(.5) + .setScrollFactor(0) + .setDepth(99); + + // Create inventory UI (hidden initially) + this.createInventoryUI(); + + // Add click event to toggle inventory + this.backpackIcon.setInteractive(); + this.backpackIcon.on('pointerdown', () => { + console.log('hit'); + this.toggleInventory(); + }); + } + + createInventoryUI() { + const scene = this.scene; + + // Create inventory background + this.inventoryUI = scene.add.rectangle( + scene.game.config.width / 2, + scene.game.config.height / 2, + 400, + 300, + 0x2F4F4F + ).setOrigin(0.5).setAlpha(0.9).setScrollFactor(0).setDepth(150); + + // Create close button + this.closeButton = scene.add.rectangle( + scene.game.config.width / 2 + 180, + scene.game.config.height / 2 - 130, + 30, + 30, + 0xFF4444 + ).setOrigin(0.5).setScrollFactor(0).setDepth(150); + + // Add X to close button + this.closeButtonText = scene.add.text( + scene.game.config.width / 2 + 180, + scene.game.config.height / 2 - 130, + 'X', + { fontSize: '24px', fill: '#FFFFFF' } + ).setOrigin(0.5).setScrollFactor(0).setDepth(150); + + // Hide inventory UI initially + this.inventoryUI.setVisible(false); + this.closeButton.setVisible(false); + this.closeButtonText.setVisible(false); + + // Add click event to close button + this.closeButton.setInteractive(); + this.closeButton.on('pointerdown', () => { + this.toggleInventory(); + }); + } + + toggleInventory() { + this.isOpen = !this.isOpen; + + if (this.isOpen) { + this.scene.player.stop(); + this.scene.player.canMove = false; + // Show inventory UI and close button + this.inventoryUI.setVisible(true); + this.closeButton.setVisible(true); + this.closeButtonText.setVisible(true); + } else { + // Hide inventory UI and close button + this.inventoryUI.setVisible(false); + this.closeButton.setVisible(false); + this.closeButtonText.setVisible(false); + this.scene.time.delayedCall(200, () => { + this.scene.player.canMove = true; + }); + } + } + + addItem(item) { + const type = item.props.type; + const amount = item.props.amount; + if (type in this.inventory) { + this.inventory[type] += amount; + } else { + this.inventory[type] = amount; + } + this.scene.tweens.add({ + targets: this.backpackIcon, + angle: 360, + delay: 500, + duration: 500 + }); + this.scene.tweens.add({ + targets: this.backpackIcon, + scale: 0.8, + delay: 500, + yoyo: true, + ease: 'Bounce', + duration: 500 + }); + } + + removeItem(item) { + const index = this.inventory.indexOf(item); + if (index > -1) { + this.inventory.splice(index, 1); + } + } + + update(delta) { + // Inventory update logic can go here + } +} \ No newline at end of file diff --git a/src/objects.js b/src/objects.js index 9b428d3..8b64c51 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2,6 +2,7 @@ export class ObjectManager { constructor(scene) { this.scene = scene; this.objects = []; + this.healthBars = new Map(); // Store references to health bars for cleanup } init() { @@ -37,58 +38,182 @@ export class ObjectManager { const palmTree = this.scene.physics.add.sprite(x, y, 'objects', 0); this.scene.objects.add(palmTree); palmTree.setImmovable(true).setSize(60,100); + palmTree.props = { + type: 'palmTree', + health: 100, + fullHealth: 100, + yield: 'wood', + amount: [0,1], + doneSprite: 10 + }; + this.createHealthBar(palmTree); } else if (key === 'forestTree' && rand <= objectType[key]) { const forestRand = Phaser.Math.Between(1,4); const forestTree = this.scene.physics.add.sprite(x, y, 'objects', forestRand); this.scene.objects.add(forestTree); forestTree.setImmovable(true).setSize(50,100); + forestTree.props = { + type: 'forestTree', + health: 200, + fullHealth: 200, + yield: 'wood', + amount: [0,3], + doneSprite: 11 + }; + this.createHealthBar(forestTree); } else if (key === 'boulder' && rand <= objectType[key]) { const boulder = this.scene.physics.add.sprite(x, y, 'objects', 20); this.scene.objects.add(boulder); boulder.setImmovable(true); + boulder.props = { + type: 'boulder', + health: 200, + fullHealth: 200, + yield: 'rock', + amount: [1,3], + doneSprite: null + }; + this.createHealthBar(boulder); } }); } - createEnemy(x, y, properties) { - // Placeholder for enemy creation logic - console.log(`Creating enemy at (${x}, ${y}) with properties:`, properties); + createHealthBar(object) { + // Create a graphics object for the health bar + const healthBar = this.scene.add.graphics(); - // Example implementation: - // const enemy = this.scene.physics.add.sprite( - // x * tileWidth + tileWidth / 2, - // y * tileHeight + tileHeight / 2, - // 'enemy-sprite' - // ); - // this.objects.push(enemy); + // Position the health bar above the object (adjust offset as needed) + const offsetX = 0; + const offsetY = -50; // Adjust to position above the object + + // Store reference to health bar for updates and cleanup + object.healthBar = healthBar; + + // Add to our map for cleanup + this.healthBars.set(object, healthBar); + + // Draw initial health bar (will be updated in update loop) + this.updateHealthBar(object, true); } - createCollectible(x, y, properties) { - // Placeholder for collectible creation logic - console.log(`Creating collectible at (${x}, ${y}) with properties:`, properties); + updateHealthBar(object, justCreated) { + if (!object.healthBar || !object.props) return; - // Example implementation: - // const collectible = this.scene.physics.add.sprite( - // x * tileWidth + tileWidth / 2, - // y * tileHeight + tileHeight / 2, - // 'collectible-sprite' - // ); - // this.objects.push(collectible); + const health = object.props.health; + const fullHealth = object.props.fullHealth; + + // Calculate health percentage + const healthPercentage = Math.max(0, Math.min(1, health / fullHealth)); + + // Clear previous graphics + object.healthBar.clear(); + + // Health bar dimensions + const barWidth = 60; + const barHeight = 8; + const offsetX = -barWidth / 2; // Center the bar on object + const offsetY = -50; // Position above object + + // Draw background (red) - full health bar + object.healthBar.fillStyle(0xff0000, 1); + object.healthBar.fillRect(object.x + offsetX, object.y + offsetY, barWidth, barHeight).setAlpha(1); + + // Draw health fill (green) - current health + const greenWidth = barWidth * healthPercentage; + if (greenWidth > 0) { + object.healthBar.fillStyle(0x00ff00, 1); + object.healthBar.fillRect(object.x + offsetX, object.y + offsetY, greenWidth, barHeight).setAlpha(1); + } + if (justCreated) { + object.healthBar.setAlpha(0); + } + + if (object.alpha > 0) { + this.scene.tweens.add({ + targets: object.healthBar, + alpha: 0, + delay: 10000 + }); + } + + if (healthPercentage <= 0) { + object.healthBar.destroy(); + } } - createPlatform(x, y, properties) { - // Placeholder for platform creation logic - console.log(`Creating platform at (${x}, ${y}) with properties:`, properties); + takeDamage(object, player) { + const dmg = Phaser.Math.Between(player.dmgMin,player.dmgMax); + if (player.targetX === object.x && player.targetY === object.y) { + switch (object.props.type){ + case 'palmTree': + object.props.health -= dmg; + break; + case 'forestTree': + object.props.health -= dmg; + break; + default: + } + this.updateHealthBar(object); + if (object.props.health <= 0) { + this.destroyObject(object); + } + } + } + + destroyObject(object) { + // Replace object with doneSprite + if (object.props.doneSprite !== null) { + const doneSprite = this.scene.physics.add.sprite(object.x, object.y, 'objects', object.props.doneSprite); + + } + + // Drop Resources + if (object.props.yield) { + const amount = Phaser.Math.Between(object.props.amount[0], object.props.amount[1]); + if (amount > 0) { + let sprite = null; + switch (object.props.yield) { + case 'wood': + if (amount === 3) { + sprite = 14; + } else if (amount === 2) { + sprite = 13; + } else { + sprite = 12; + } + + break; + default: + } + const drop = this.scene.physics.add.sprite(object.x-5, object.y-50, 'objects', sprite).setScale(.8); + this.scene.tweens.add({ + targets: drop, + scale: 1, + y: object.y, + x: object.x, + ease: 'Bounce', + duration: 800, + onComplete: () => { + drop.postFX.addGlow(0x00ffc6); + this.scene.items.add(drop); + drop.props = { + type: object.props.yield, + amount: amount + }; + drop.setInteractive(); + } + }); + } + } + + // Remove from scene and cleanup + object.destroy(); - // Example implementation: - // const platform = this.scene.physics.add.sprite( - // x * tileWidth + tileWidth / 2, - // y * tileHeight + tileHeight / 2, - // 'platform-sprite' - // ); - // platform.setImmovable(true); - // this.objects.push(platform); + // Remove health bar reference + if (object.healthBar && this.healthBars.has(object)) { + this.healthBars.delete(object); + } } update(delta) { @@ -97,6 +222,11 @@ export class ObjectManager { if (object.update) { object.update(delta); } + + // Update health bars for objects that have them + if (object.healthBar && object.props) { + this.updateHealthBar(object); + } }); } } \ No newline at end of file diff --git a/src/player.js b/src/player.js index cf7cd08..9a04397 100644 --- a/src/player.js +++ b/src/player.js @@ -7,16 +7,23 @@ export class Player extends Phaser.GameObjects.Sprite { scene.physics.world.enable(this); this.body.setCollideWorldBounds(true); this.body.setSize(50,100); + this.setDepth(100); // Set player properties this.speed = 200; this.isMoving = false; this.targetX = x; this.targetY = y; + this.dmgMin = 5; + this.dmgMax = 25; + this.inventory = {}; + this.canMove = true; // Add input listener for mouse clicks scene.input.on('pointerdown', (pointer) => { - this.moveToPoint(pointer.worldX, pointer.worldY); + if (this.canMove) { + this.moveToPoint(pointer.worldX, pointer.worldY); + } }); // Create animations @@ -56,15 +63,15 @@ export class Player extends Phaser.GameObjects.Sprite { moveToPoint(x, y) { // Set target position - this.targetX = x; - this.targetY = y; - + this.targetX = Math.floor(x / 100) * 100 + 50; + this.targetY = Math.floor(y / 100) * 100 + 50; + // Calculate direction to target - const dx = x - this.x; - const dy = y - this.y; + const dx = this.targetX - this.x; + const dy = this.targetY - this.y; const distance = Math.sqrt(dx * dx + dy * dy); - if (distance > 100) { // Only move if target is far enough + if (distance > 50) { // Only move if target is far enough this.isMoving = true; // Set velocity towards target @@ -142,7 +149,7 @@ export class Player extends Phaser.GameObjects.Sprite { // Method to stop player movement stop() { - this.setVelocity(0, 0); + this.body.setVelocity(0, 0); this.isMoving = false; // Play idle animation when stopping diff --git a/src/scenes/Game.js b/src/scenes/Game.js index aa5e1c1..c9bc317 100644 --- a/src/scenes/Game.js +++ b/src/scenes/Game.js @@ -1,6 +1,7 @@ import { Player } from '../player.js'; import { CycleManager } from '../cycle.js'; import { ObjectManager } from '../objects.js'; +import { InventoryManager } from '../inventory.js'; export class Game extends Phaser.Scene { constructor() { @@ -11,22 +12,20 @@ export class Game extends Phaser.Scene { } preload() { - // Load player sprite + this.load.tilemapTiledJSON('game-map', 'assets/game-map.json'); this.load.spritesheet('player', 'assets/images/player.png', { frameWidth: 100, frameHeight: 100 }); - - // Load objects sprite this.load.spritesheet('objects', 'assets/images/objects.png', { frameWidth: 100, frameHeight: 100 }); - - // Load tilemap - this.load.tilemapTiledJSON('game-map', 'assets/game-map.json'); - // Load tileset image + this.load.image('terrain-tileset', 'assets/images/terrain.png'); + this.load.image('backpack', 'assets/images/backpack.png'); + + this.load.font('eraserDust', 'assets/fonts/EraserDust.ttf'); } create() { @@ -47,15 +46,41 @@ export class Game extends Phaser.Scene { // Initialize object manager this.objects = this.physics.add.group(); + this.items = this.physics.add.group(); this.objectManager = new ObjectManager(this); this.objectManager.init(); // Create player at center of screen this.player = new Player(this, 1600, 3100); + // Initialize inventory manager + this.inventoryManager = new InventoryManager(this); + this.inventoryManager.init(); + // Physics Collisions this.physics.add.collider(this.player, this.mainLayer); - this.physics.add.collider(this.player, this.objects); + this.physics.add.collider(this.player, this.objects, (player, object) => { + player.stop(); + this.objectManager.takeDamage(object, player); + }); + this.physics.add.collider(this.player, this.items, (player, item) => { + if (item.props.collected === true) { + return; + } + item.props.collected = true; + player.stop(); + this.inventoryManager.addItem(item); + const cam = this.cameras.main.worldView; + this.tweens.add({ + targets: item, + x: cam.x+cam.width-150, + y: cam.y+cam.height-150, + duration: 500, + onComplete: () => { + item.destroy(); + } + }); + }); // Make camera follow the player this.cameras.main.startFollow(this.player); @@ -86,5 +111,9 @@ export class Game extends Phaser.Scene { if (this.objectManager) { this.objectManager.update(delta); } + + if (this.inventoryManager) { + this.inventoryManager.update(delta); + } } } \ No newline at end of file