/** * TransportSystem.js * ================== * Manages all transportation methods in the game * - Horses (5 breeds, different speeds) * - Carts & Wagons (cargo capacity) * - Trains (track-based, high speed, high capacity) * - Water transport (kayak, SUP, raft, boat) * - Bicycles & Boards (longboard, mountain board, snowboard) * * Uses: transportation_vehicles_detailed_1766097668396.tsx * train_repairs_3_states (broken → repairing → rideable) * * @author NovaFarma Team * @date 2025-12-22 */ export default class TransportSystem { constructor(scene) { this.scene = scene; // Current state this.currentVehicle = null; this.ownedVehicles = new Map(); this.trainTracks = []; // Vehicle definitions this.vehicleData = this.defineVehicles(); // Train repair state this.trainRepairProgress = 0; console.log('🚂 TransportSystem initialized'); } defineVehicles() { return { // ===== HORSES ===== horse_basic: { name: 'Basic Horse', type: 'mount', speed: 200, capacity: 0, cost: 500, unlockLevel: 3, sprite: 'horse_brown', waterAllowed: false, description: 'Reliable companion for travel' }, horse_fast: { name: 'Racing Horse', type: 'mount', speed: 300, capacity: 0, cost: 1500, unlockLevel: 7, sprite: 'horse_white', waterAllowed: false, description: 'Swift steed for long distances' }, horse_draft: { name: 'Draft Horse', type: 'mount', speed: 150, capacity: 50, cost: 1200, unlockLevel: 5, sprite: 'horse_black', waterAllowed: false, description: 'Strong horse, can carry cargo' }, // ===== CARTS & WAGONS ===== cart_wooden: { name: 'Wooden Cart', type: 'vehicle', speed: 120, capacity: 100, cost: 800, unlockLevel: 4, blueprint: 'blueprint_cart', sprite: 'cart_wooden', waterAllowed: false, requiresHorse: true, description: 'Transport goods efficiently' }, wagon_large: { name: 'Large Wagon', type: 'vehicle', speed: 100, capacity: 250, cost: 2500, unlockLevel: 8, blueprint: 'blueprint_wagon', sprite: 'wagon_large', waterAllowed: false, requiresHorse: true, description: 'Massive cargo capacity' }, // ===== TRAINS ===== train: { name: 'Steam Train', type: 'rail', speed: 400, capacity: 500, cost: 10000, unlockLevel: 15, blueprint: 'blueprint_train', sprite: 'train_rideable', waterAllowed: false, requiresTrack: true, repairStages: ['broken', 'repairing', 'rideable'], description: 'Ultimate transport - requires tracks' }, // ===== WATER TRANSPORT ===== kayak: { name: 'Kayak', type: 'water', speed: 150, capacity: 20, cost: 300, unlockLevel: 6, sprite: 'kayak', waterOnly: true, description: 'Navigate rivers and lakes' }, sup: { name: 'Stand-Up Paddleboard', type: 'water', speed: 100, capacity: 5, cost: 200, unlockLevel: 5, sprite: 'sup', waterOnly: true, description: 'Calm water exploration' }, raft: { name: 'Wooden Raft', type: 'water', speed: 80, capacity: 50, cost: 150, unlockLevel: 3, sprite: 'raft', waterOnly: true, description: 'Basic water transport' }, boat: { name: 'Fishing Boat', type: 'water', speed: 180, capacity: 100, cost: 1000, unlockLevel: 10, blueprint: 'blueprint_boat', sprite: 'boat', waterOnly: true, description: 'Ocean-ready vessel' }, // ===== BICYCLES & BOARDS ===== bicycle: { name: 'Bicycle', type: 'manual', speed: 180, capacity: 10, cost: 250, unlockLevel: 2, sprite: 'bicycle', waterAllowed: false, description: 'Energy-efficient travel' }, longboard: { name: 'Longboard', type: 'manual', speed: 220, capacity: 0, cost: 150, unlockLevel: 4, sprite: 'longboard', waterAllowed: false, terrainBonus: { 'road': 1.5 }, // 50% faster on roads description: 'Cruise downhill paths' }, mountain_board: { name: 'Mountain Board', type: 'manual', speed: 200, capacity: 5, cost: 300, unlockLevel: 6, sprite: 'mountain_board', waterAllowed: false, terrainBonus: { 'mountain': 1.3 }, description: 'Off-road board for rough terrain' }, snowboard: { name: 'Snowboard', type: 'manual', speed: 250, capacity: 0, cost: 200, unlockLevel: 5, sprite: 'snowboard', waterAllowed: false, terrainRequired: 'snow', terrainBonus: { 'snow': 2.0 }, // 2x speed on snow description: 'Winter transport - snow only' } }; } // ===== VEHICLE MOUNTING ===== canMount(vehicleId, playerX, playerY) { const vehicle = this.vehicleData[vehicleId]; if (!vehicle) { return { canMount: false, reason: 'Vehicle not found' }; } // Check if player owns this vehicle if (!this.ownedVehicles.has(vehicleId)) { return { canMount: false, reason: 'You don\'t own this vehicle' }; } // Check if already mounted if (this.currentVehicle) { return { canMount: false, reason: 'Already using a vehicle' }; } // Check level requirement const playerLevel = this.scene.player?.stats?.level || 1; if (playerLevel < vehicle.unlockLevel) { return { canMount: false, reason: `Requires level ${vehicle.unlockLevel}` }; } // Check terrain compatibility const currentTile = this.getTileAt(playerX, playerY); if (vehicle.waterOnly && !currentTile.properties?.isWater) { return { canMount: false, reason: 'Water vehicles require water' }; } if (!vehicle.waterAllowed && currentTile.properties?.isWater) { return { canMount: false, reason: 'Cannot use on water' }; } if (vehicle.terrainRequired && currentTile.properties?.terrain !== vehicle.terrainRequired) { return { canMount: false, reason: `Requires ${vehicle.terrainRequired} terrain` }; } // Check if train requires tracks if (vehicle.requiresTrack && !this.isOnTrack(playerX, playerY)) { return { canMount: false, reason: 'Train requires tracks' }; } // Check if cart/wagon requires horse if (vehicle.requiresHorse && !this.hasHorse()) { return { canMount: false, reason: 'Requires a horse to pull' }; } return { canMount: true }; } mount(vehicleId) { const check = this.canMount(vehicleId, this.scene.player.x, this.scene.player.y); if (!check.canMount) { this.scene.uiSystem?.showError(check.reason); return false; } const vehicle = this.vehicleData[vehicleId]; // Update player speed this.scene.player.baseSpeed = this.scene.player.baseSpeed || 100; this.scene.player.setMaxVelocity(vehicle.speed); // Change player sprite (riding animation) const originalTexture = this.scene.player.texture.key; this.scene.player.setTexture(`player_on_${vehicle.type}`); // Store original state this.currentVehicle = { id: vehicleId, data: vehicle, originalSpeed: this.scene.player.baseSpeed, originalTexture: originalTexture, mountTime: Date.now() }; // Show UI this.scene.uiSystem?.showVehicleUI({ name: vehicle.name, speed: vehicle.speed, capacity: vehicle.capacity, currentCargo: 0 }); // Emit event this.scene.events.emit('vehicleMounted', { vehicleId, vehicle }); console.log(`🚴 Mounted ${vehicle.name} (${vehicle.speed} speed)`); return true; } dismount() { if (!this.currentVehicle) { return false; } const vehicle = this.currentVehicle.data; // Restore player speed this.scene.player.setMaxVelocity(this.currentVehicle.originalSpeed); // Restore player sprite this.scene.player.setTexture(this.currentVehicle.originalTexture); // Hide UI this.scene.uiSystem?.hideVehicleUI(); // Emit event this.scene.events.emit('vehicleDismounted', { vehicleId: this.currentVehicle.id, rideDuration: Date.now() - this.currentVehicle.mountTime }); console.log(`🚶 Dismounted ${vehicle.name}`); this.currentVehicle = null; return true; } // ===== VEHICLE PURCHASING ===== canPurchase(vehicleId) { const vehicle = this.vehicleData[vehicleId]; if (!vehicle) { return { canPurchase: false, reason: 'Vehicle not found' }; } // Check if already owned if (this.ownedVehicles.has(vehicleId)) { return { canPurchase: false, reason: 'Already owned' }; } // Check level const playerLevel = this.scene.player?.stats?.level || 1; if (playerLevel < vehicle.unlockLevel) { return { canPurchase: false, reason: `Requires level ${vehicle.unlockLevel}` }; } // Check blueprint (if required) if (vehicle.blueprint && this.scene.recipeSystem) { if (!this.scene.recipeSystem.unlockedRecipes.has(vehicle.blueprint)) { return { canPurchase: false, reason: 'Blueprint not unlocked' }; } } // Check gold const playerGold = this.getPlayerGold(); if (playerGold < vehicle.cost) { return { canPurchase: false, reason: `Need ${vehicle.cost} gold (have ${playerGold})` }; } return { canPurchase: true }; } purchase(vehicleId) { const check = this.canPurchase(vehicleId); if (!check.canPurchase) { this.scene.uiSystem?.showError(check.reason); return false; } const vehicle = this.vehicleData[vehicleId]; // Deduct gold this.removeGold(vehicle.cost); // Add to owned vehicles this.ownedVehicles.set(vehicleId, { purchaseDate: Date.now(), totalDistance: 0, totalTime: 0 }); // Save this.saveOwnedVehicles(); // Notification this.scene.uiSystem?.showNotification({ title: '🚗 Vehicle Purchased!', message: `${vehicle.name} is now yours!`, icon: vehicle.sprite, duration: 4000, color: '#00FF00' }); // Emit event this.scene.events.emit('vehiclePurchased', { vehicleId, vehicle }); console.log(`💰 Purchased ${vehicle.name} for ${vehicle.cost} gold`); return true; } // ===== TRAIN REPAIR SYSTEM ===== repairTrain(workAmount = 10) { if (this.trainRepairProgress >= 100) { this.scene.uiSystem?.showError('Train already repaired!'); return false; } // Add progress this.trainRepairProgress += workAmount; this.trainRepairProgress = Math.min(100, this.trainRepairProgress); // Get current state const state = this.getTrainRepairState(); // Show progress this.scene.uiSystem?.showNotification({ title: 'Train Repair', message: `${state} - ${this.trainRepairProgress.toFixed(0)}%`, icon: 'train', duration: 2000 }); // Check if just completed if (this.trainRepairProgress === 100 && state === 'Rideable') { this.unlockTrain(); } // Emit event this.scene.events.emit('trainRepairProgress', { progress: this.trainRepairProgress, state }); return true; } getTrainRepairState() { if (this.trainRepairProgress < 33) { return 'Broken'; } else if (this.trainRepairProgress < 100) { return 'Repairing'; } else { return 'Rideable'; } } unlockTrain() { // Add train to owned vehicles this.ownedVehicles.set('train', { purchaseDate: Date.now(), totalDistance: 0, totalTime: 0 }); this.saveOwnedVehicles(); // Big notification this.scene.uiSystem?.showNotification({ title: '🎉 TRAIN REPAIRED!', message: 'The steam train is ready to ride!', icon: 'train', duration: 6000, color: '#FFD700' }); this.scene.events.emit('trainUnlocked'); console.log('🚂 Train fully repaired and unlocked!'); } // ===== TERRAIN BONUS SYSTEM ===== getSpeedModifier(x, y) { if (!this.currentVehicle) return 1.0; const vehicle = this.currentVehicle.data; const tile = this.getTileAt(x, y); const terrain = tile.properties?.terrain; if (vehicle.terrainBonus && terrain && vehicle.terrainBonus[terrain]) { return vehicle.terrainBonus[terrain]; } return 1.0; } getCurrentSpeed() { if (!this.currentVehicle) { return this.scene.player?.baseSpeed || 100; } const baseSpeed = this.currentVehicle.data.speed; const modifier = this.getSpeedModifier(this.scene.player.x, this.scene.player.y); return baseSpeed * modifier; } // ===== HELPER FUNCTIONS ===== hasHorse() { return this.ownedVehicles.has('horse_basic') || this.ownedVehicles.has('horse_fast') || this.ownedVehicles.has('horse_draft'); } isOnTrack(x, y) { // Check if position has train tracks const tile = this.getTileAt(x, y); return tile.properties?.hasTrack || false; } getTileAt(x, y) { // Get tile from terrain system if (this.scene.terrainSystem) { return this.scene.terrainSystem.getTileAtWorldXY(x, y); } // Fallback return { properties: {} }; } getPlayerGold() { if (this.scene.inventorySystem) { return this.scene.inventorySystem.getQuantity('gold'); } return this.scene.player?.inventory?.gold || 0; } removeGold(amount) { if (this.scene.inventorySystem) { this.scene.inventorySystem.removeItem('gold', amount); } else { this.scene.player.inventory.gold -= amount; } } // ===== GETTERS ===== getVehicle(vehicleId) { return this.vehicleData[vehicleId]; } getAllVehicles() { return Object.entries(this.vehicleData).map(([id, data]) => ({ id, ...data, owned: this.ownedVehicles.has(id) })); } getOwnedVehicles() { return Array.from(this.ownedVehicles.keys()); } getVehiclesByType(type) { return this.getAllVehicles().filter(v => v.type === type); } getPurchasableVehicles() { return this.getAllVehicles().filter(v => this.canPurchase(v.id).canPurchase ); } getCurrentVehicle() { return this.currentVehicle; } isRiding() { return this.currentVehicle !== null; } // ===== DATA PERSISTENCE ===== saveOwnedVehicles() { const data = { owned: Array.from(this.ownedVehicles.entries()), trainProgress: this.trainRepairProgress }; localStorage.setItem('ownedVehicles', JSON.stringify(data)); } loadOwnedVehicles() { const saved = localStorage.getItem('ownedVehicles'); if (saved) { const data = JSON.parse(saved); this.ownedVehicles = new Map(data.owned); this.trainRepairProgress = data.trainProgress || 0; console.log('🚗 Loaded', this.ownedVehicles.size, 'owned vehicles'); } } // ===== UPDATE ===== update(time, delta) { if (this.currentVehicle) { // Track distance and time const deltaSeconds = delta / 1000; const speed = this.getCurrentSpeed(); const distance = (speed * deltaSeconds) / 60; // Approximate const stats = this.ownedVehicles.get(this.currentVehicle.id); if (stats) { stats.totalDistance += distance; stats.totalTime += deltaSeconds; } // Update speed modifier based on terrain const modifier = this.getSpeedModifier(this.scene.player.x, this.scene.player.y); if (modifier !== 1.0) { this.scene.player.setMaxVelocity(this.currentVehicle.data.speed * modifier); } } } // ===== CLEANUP ===== destroy() { this.saveOwnedVehicles(); this.ownedVehicles.clear(); this.trainTracks = []; this.currentVehicle = null; } }