dodatki
This commit is contained in:
193
src/systems/MountSystem.js
Normal file
193
src/systems/MountSystem.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* MOUNT SYSTEM
|
||||
* Handles rideable animals (Donkey, Horse, etc.)
|
||||
*/
|
||||
class MountSystem {
|
||||
constructor(scene, player) {
|
||||
this.scene = scene;
|
||||
this.player = player;
|
||||
this.currentMount = null;
|
||||
this.isMounted = false;
|
||||
|
||||
// Mount definitions
|
||||
this.mountData = {
|
||||
'donkey': {
|
||||
name: 'Donkey',
|
||||
speed: 200, // Movement speed when mounted
|
||||
texture: 'donkey',
|
||||
inventorySlots: 10, // Extra inventory from saddlebags
|
||||
color: 0x8B7355 // Brown
|
||||
},
|
||||
'horse': {
|
||||
name: 'Horse',
|
||||
speed: 300,
|
||||
texture: 'horse',
|
||||
inventorySlots: 5,
|
||||
color: 0x654321
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn a mount in the world
|
||||
*/
|
||||
spawnMount(gridX, gridY, type) {
|
||||
if (!this.mountData[type]) {
|
||||
console.error('Unknown mount type:', type);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = this.mountData[type];
|
||||
const screenPos = this.scene.iso.toScreen(gridX, gridY);
|
||||
|
||||
const mount = {
|
||||
type,
|
||||
gridX,
|
||||
gridY,
|
||||
sprite: null,
|
||||
inventory: [], // Saddlebag storage
|
||||
tamed: true
|
||||
};
|
||||
|
||||
// Create sprite
|
||||
mount.sprite = this.scene.add.sprite(
|
||||
screenPos.x + this.scene.terrainOffsetX,
|
||||
screenPos.y + this.scene.terrainOffsetY,
|
||||
data.texture || 'npc_zombie' // Fallback
|
||||
);
|
||||
mount.sprite.setOrigin(0.5, 1);
|
||||
mount.sprite.setScale(0.4);
|
||||
mount.sprite.setTint(data.color);
|
||||
mount.sprite.setDepth(this.scene.iso.getDepth(gridX, gridY, this.scene.iso.LAYER_OBJECTS));
|
||||
|
||||
mount.sprite.setInteractive({ useHandCursor: true });
|
||||
mount.sprite.on('pointerdown', () => {
|
||||
this.mountUp(mount);
|
||||
});
|
||||
|
||||
console.log(`🐴 Spawned ${data.name} at ${gridX},${gridY}`);
|
||||
return mount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount up on a donkey/horse
|
||||
*/
|
||||
mountUp(mount) {
|
||||
if (this.isMounted) {
|
||||
console.log('Already mounted!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentMount = mount;
|
||||
this.isMounted = true;
|
||||
|
||||
const data = this.mountData[mount.type];
|
||||
|
||||
// Hide mount sprite
|
||||
mount.sprite.setVisible(false);
|
||||
|
||||
// Increase player speed
|
||||
if (this.player) {
|
||||
this.player.originalSpeed = this.player.speed || 100;
|
||||
this.player.speed = data.speed;
|
||||
}
|
||||
|
||||
// Visual feedback
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: this.player.sprite.x,
|
||||
y: this.player.sprite.y - 40,
|
||||
text: `🐴 Mounted ${data.name}!`,
|
||||
color: '#ffaa00'
|
||||
});
|
||||
|
||||
console.log(`🐴 Mounted on ${data.name}! Speed: ${data.speed}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismount
|
||||
*/
|
||||
dismount() {
|
||||
if (!this.isMounted || !this.currentMount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mount = this.currentMount;
|
||||
const data = this.mountData[mount.type];
|
||||
|
||||
// Restore player speed
|
||||
if (this.player && this.player.originalSpeed) {
|
||||
this.player.speed = this.player.originalSpeed;
|
||||
}
|
||||
|
||||
// Place mount at player location
|
||||
const playerPos = this.player.getPosition();
|
||||
mount.gridX = Math.round(playerPos.x);
|
||||
mount.gridY = Math.round(playerPos.y);
|
||||
|
||||
const screenPos = this.scene.iso.toScreen(mount.gridX, mount.gridY);
|
||||
mount.sprite.setPosition(
|
||||
screenPos.x + this.scene.terrainOffsetX,
|
||||
screenPos.y + this.scene.terrainOffsetY
|
||||
);
|
||||
mount.sprite.setVisible(true);
|
||||
|
||||
// Visual feedback
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: this.player.sprite.x,
|
||||
y: this.player.sprite.y - 40,
|
||||
text: `Dismounted`,
|
||||
color: '#888888'
|
||||
});
|
||||
|
||||
this.currentMount = null;
|
||||
this.isMounted = false;
|
||||
|
||||
console.log(`🐴 Dismounted from ${data.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle mount/dismount (E key)
|
||||
*/
|
||||
toggleMount() {
|
||||
if (this.isMounted) {
|
||||
this.dismount();
|
||||
} else {
|
||||
// Try to find nearby mount
|
||||
const nearbyMount = this.findNearbyMount();
|
||||
if (nearbyMount) {
|
||||
this.mountUp(nearbyMount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findNearbyMount() {
|
||||
// TODO: Search for mounts near player
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access saddlebag inventory
|
||||
*/
|
||||
openSaddlebag() {
|
||||
if (!this.isMounted || !this.currentMount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = this.mountData[this.currentMount.type];
|
||||
console.log(`🎒 Opening saddlebag (${data.inventorySlots} slots)`);
|
||||
return this.currentMount.inventory;
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
// Update mount position if mounted
|
||||
if (this.isMounted && this.currentMount && this.player) {
|
||||
const playerPos = this.player.getPosition();
|
||||
this.currentMount.gridX = Math.round(playerPos.x);
|
||||
this.currentMount.gridY = Math.round(playerPos.y);
|
||||
}
|
||||
}
|
||||
|
||||
isMountedOnAnything() {
|
||||
return this.isMounted;
|
||||
}
|
||||
}
|
||||
163
src/systems/PerennialCropSystem.js
Normal file
163
src/systems/PerennialCropSystem.js
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* PERENNIAL CROPS SYSTEM
|
||||
* Handles multi-season crops like Apple Trees
|
||||
*/
|
||||
class PerennialCropSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.perennials = []; // Array of perennial objects
|
||||
|
||||
// Perennial definitions
|
||||
this.perennialData = {
|
||||
'apple_tree': {
|
||||
name: 'Apple Tree',
|
||||
growthTime: 120000, // 2 minutes to maturity
|
||||
harvestSeason: 'autumn', // Only harvest in autumn
|
||||
harvestYield: { 'apple': 5 },
|
||||
regrowthTime: 30000, // 30s to regrow apples
|
||||
texture: 'apple_tree',
|
||||
stages: ['sapling', 'young', 'mature', 'fruiting']
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Plant a perennial crop
|
||||
*/
|
||||
plant(gridX, gridY, type) {
|
||||
if (!this.perennialData[type]) {
|
||||
console.error('Unknown perennial type:', type);
|
||||
return false;
|
||||
}
|
||||
|
||||
const perennial = {
|
||||
type,
|
||||
gridX,
|
||||
gridY,
|
||||
plantedTime: Date.now(),
|
||||
stage: 0, // 0=sapling, 1=young, 2=mature, 3=fruiting
|
||||
canHarvest: false,
|
||||
sprite: null,
|
||||
healthBar: null
|
||||
};
|
||||
|
||||
this.perennials.push(perennial);
|
||||
this.createSprite(perennial);
|
||||
console.log(`🌳 Planted ${type} at ${gridX},${gridY}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
createSprite(perennial) {
|
||||
const data = this.perennialData[perennial.type];
|
||||
const screenPos = this.scene.iso.toScreen(perennial.gridX, perennial.gridY);
|
||||
|
||||
// Create tree sprite
|
||||
perennial.sprite = this.scene.add.sprite(
|
||||
screenPos.x + this.scene.terrainOffsetX,
|
||||
screenPos.y + this.scene.terrainOffsetY,
|
||||
data.texture || 'tree'
|
||||
);
|
||||
perennial.sprite.setOrigin(0.5, 1);
|
||||
perennial.sprite.setScale(0.5);
|
||||
perennial.sprite.setDepth(this.scene.iso.getDepth(perennial.gridX, perennial.gridY, this.scene.iso.LAYER_OBJECTS));
|
||||
|
||||
this.updateSprite(perennial);
|
||||
}
|
||||
|
||||
updateSprite(perennial) {
|
||||
const data = this.perennialData[perennial.type];
|
||||
|
||||
// Update tint based on stage
|
||||
if (perennial.stage === 0) {
|
||||
perennial.sprite.setTint(0x88aa88); // Light green (sapling)
|
||||
perennial.sprite.setScale(0.3);
|
||||
} else if (perennial.stage === 1) {
|
||||
perennial.sprite.setTint(0x44aa44); // Medium green (young)
|
||||
perennial.sprite.setScale(0.4);
|
||||
} else if (perennial.stage === 2) {
|
||||
perennial.sprite.clearTint(); // Normal (mature)
|
||||
perennial.sprite.setScale(0.5);
|
||||
} else if (perennial.stage === 3) {
|
||||
perennial.sprite.setTint(0xff8844); // Orange tint (fruiting)
|
||||
perennial.sprite.setScale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
update(delta, currentSeason) {
|
||||
this.perennials.forEach(perennial => {
|
||||
const data = this.perennialData[perennial.type];
|
||||
const age = Date.now() - perennial.plantedTime;
|
||||
|
||||
// Growth stages
|
||||
if (perennial.stage < 3) {
|
||||
const stageTime = data.growthTime / 3;
|
||||
const newStage = Math.min(3, Math.floor(age / stageTime));
|
||||
|
||||
if (newStage !== perennial.stage) {
|
||||
perennial.stage = newStage;
|
||||
this.updateSprite(perennial);
|
||||
|
||||
if (perennial.stage === 3) {
|
||||
console.log(`🌳 ${data.name} reached maturity!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fruiting logic (only in correct season and when mature)
|
||||
if (perennial.stage === 3 && currentSeason === data.harvestSeason) {
|
||||
if (!perennial.canHarvest) {
|
||||
perennial.canHarvest = true;
|
||||
perennial.sprite.setTint(0xff6644); // Bright orange (ready to harvest)
|
||||
}
|
||||
} else if (perennial.canHarvest && currentSeason !== data.harvestSeason) {
|
||||
perennial.canHarvest = false;
|
||||
perennial.sprite.clearTint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to harvest perennial
|
||||
*/
|
||||
harvest(gridX, gridY) {
|
||||
const perennial = this.perennials.find(p =>
|
||||
p.gridX === gridX && p.gridY === gridY
|
||||
);
|
||||
|
||||
if (!perennial) return null;
|
||||
|
||||
if (!perennial.canHarvest) {
|
||||
return { error: 'Not ready to harvest' };
|
||||
}
|
||||
|
||||
const data = this.perennialData[perennial.type];
|
||||
|
||||
// Reset harvest state (will regrow)
|
||||
perennial.canHarvest = false;
|
||||
perennial.sprite.setTint(0x88aa88); // Back to green
|
||||
|
||||
console.log(`🍎 Harvested ${data.name}!`);
|
||||
return data.harvestYield;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if position has perennial
|
||||
*/
|
||||
hasPerennial(gridX, gridY) {
|
||||
return this.perennials.some(p => p.gridX === gridX && p.gridY === gridY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get perennial at position
|
||||
*/
|
||||
getPerennial(gridX, gridY) {
|
||||
return this.perennials.find(p => p.gridX === gridX && p.gridY === gridY);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.perennials.forEach(p => {
|
||||
if (p.sprite) p.sprite.destroy();
|
||||
});
|
||||
this.perennials = [];
|
||||
}
|
||||
}
|
||||
194
src/systems/SteamIntegrationSystem.js
Normal file
194
src/systems/SteamIntegrationSystem.js
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* STEAM INTEGRATION SYSTEM
|
||||
* Handles Steam achievements and cloud saves
|
||||
*
|
||||
* NOTE: Requires Steamworks SDK and Greenworks library
|
||||
* This is a placeholder/mock implementation for development
|
||||
*/
|
||||
class SteamIntegrationSystem {
|
||||
constructor() {
|
||||
this.steamAvailable = false;
|
||||
this.achievements = {};
|
||||
this.cloudSaveEnabled = false;
|
||||
|
||||
// Mock achievement definitions
|
||||
this.achievementDefs = {
|
||||
'FIRST_HARVEST': { name: 'First Harvest', desc: 'Harvest your first crop' },
|
||||
'GOLD_RUSH': { name: 'Gold Rush', desc: 'Earn 1000 gold coins' },
|
||||
'ZOMBIE_SLAYER': { name: 'Zombie Slayer', desc: 'Kill 100 zombies' },
|
||||
'MASTER_FARMER': { name: 'Master Farmer', desc: 'Harvest 1000 crops' },
|
||||
'DAY_30': { name: 'Survivor', desc: 'Survive 30 days' },
|
||||
'GREENHOUSE': { name: 'Greenhouse Builder', desc: 'Build a greenhouse' },
|
||||
'TAMED_ZOMBIE': { name: 'Zombie Tamer', desc: 'Tame your first zombie' },
|
||||
'OCEAN_EXPLORER': { name: 'Ocean Explorer', desc: 'Discover 5 islands' }
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Check if Steamworks is available
|
||||
if (typeof greenworks !== 'undefined') {
|
||||
try {
|
||||
greenworks.init();
|
||||
this.steamAvailable = true;
|
||||
this.cloudSaveEnabled = greenworks.isCloudEnabled();
|
||||
console.log('✅ Steam Integration: Active');
|
||||
console.log(`☁️ Cloud Saves: ${this.cloudSaveEnabled ? 'Enabled' : 'Disabled'}`);
|
||||
} catch (e) {
|
||||
console.warn('⚠️ Steam Integration: Failed to initialize', e);
|
||||
this.steamAvailable = false;
|
||||
}
|
||||
} else {
|
||||
console.log('ℹ️ Steam Integration: Not available (running outside Steam)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock achievement
|
||||
*/
|
||||
unlockAchievement(achievementId) {
|
||||
if (!this.achievementDefs[achievementId]) {
|
||||
console.error('Unknown achievement:', achievementId);
|
||||
return false;
|
||||
}
|
||||
|
||||
const achievement = this.achievementDefs[achievementId];
|
||||
|
||||
if (this.steamAvailable && typeof greenworks !== 'undefined') {
|
||||
try {
|
||||
greenworks.activateAchievement(achievementId, () => {
|
||||
console.log(`🏆 Achievement Unlocked: ${achievement.name}`);
|
||||
}, (err) => {
|
||||
console.error('Failed to unlock achievement:', err);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Steam achievement error:', e);
|
||||
}
|
||||
} else {
|
||||
// Mock/local achievement
|
||||
if (!this.achievements[achievementId]) {
|
||||
this.achievements[achievementId] = true;
|
||||
console.log(`🏆 [MOCK] Achievement Unlocked: ${achievement.name}`);
|
||||
|
||||
// Show notification
|
||||
if (this.scene && this.scene.events) {
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: 320,
|
||||
y: 100,
|
||||
text: `🏆 ${achievement.name}`,
|
||||
color: '#ffd700'
|
||||
});
|
||||
}
|
||||
|
||||
// Save to localStorage
|
||||
this.saveLocalAchievements();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save game to Steam Cloud
|
||||
*/
|
||||
saveToCloud(saveData) {
|
||||
if (!this.cloudSaveEnabled) {
|
||||
console.log('☁️ Cloud saves not available, saving locally');
|
||||
localStorage.setItem('novafarma_save', JSON.stringify(saveData));
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const saveString = JSON.stringify(saveData);
|
||||
greenworks.saveTextToFile('novafarma_save.json', saveString, () => {
|
||||
console.log('☁️ Game saved to Steam Cloud');
|
||||
}, (err) => {
|
||||
console.error('Failed to save to cloud:', err);
|
||||
// Fallback to local
|
||||
localStorage.setItem('novafarma_save', saveString);
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Cloud save error:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load game from Steam Cloud
|
||||
*/
|
||||
loadFromCloud(callback) {
|
||||
if (!this.cloudSaveEnabled) {
|
||||
console.log('☁️ Cloud saves not available, loading locally');
|
||||
const localSave = localStorage.getItem('novafarma_save');
|
||||
if (localSave && callback) {
|
||||
callback(JSON.parse(localSave));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
greenworks.readTextFromFile('novafarma_save.json', (data) => {
|
||||
console.log('☁️ Loaded from Steam Cloud');
|
||||
if (callback) callback(JSON.parse(data));
|
||||
}, (err) => {
|
||||
console.error('Failed to load from cloud:', err);
|
||||
// Fallback to local
|
||||
const localSave = localStorage.getItem('novafarma_save');
|
||||
if (localSave && callback) {
|
||||
callback(JSON.parse(localSave));
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Cloud load error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get achievement status
|
||||
*/
|
||||
getAchievementStatus(achievementId) {
|
||||
return this.achievements[achievementId] || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all unlocked achievements
|
||||
*/
|
||||
getUnlockedAchievements() {
|
||||
return Object.keys(this.achievements).filter(id => this.achievements[id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save achievements to localStorage (fallback)
|
||||
*/
|
||||
saveLocalAchievements() {
|
||||
localStorage.setItem('novafarma_achievements', JSON.stringify(this.achievements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load achievements from localStorage
|
||||
*/
|
||||
loadLocalAchievements() {
|
||||
const saved = localStorage.getItem('novafarma_achievements');
|
||||
if (saved) {
|
||||
this.achievements = JSON.parse(saved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set overlay position (for Steam overlay)
|
||||
*/
|
||||
activateOverlay(dialog = 'Friends') {
|
||||
if (this.steamAvailable && typeof greenworks !== 'undefined') {
|
||||
try {
|
||||
greenworks.activateGameOverlay(dialog);
|
||||
} catch (e) {
|
||||
console.error('Failed to activate Steam overlay:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global instance
|
||||
window.SteamIntegration = SteamIntegrationSystem;
|
||||
Reference in New Issue
Block a user