388 lines
11 KiB
JavaScript
388 lines
11 KiB
JavaScript
/**
|
|
* 📦 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 = [];
|
|
}
|
|
}
|