diff --git a/database/schema.sql b/database/schema.sql index 4bfb6e9..c99314b 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS users ( email VARCHAR(100) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, airline_name VARCHAR(100) DEFAULT '', - balance DECIMAL(15,2) DEFAULT 500000.00, + balance DECIMAL(15,2) DEFAULT 10000000.00, level INTEGER DEFAULT 1, experience INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, @@ -92,10 +92,55 @@ CREATE TABLE IF NOT EXISTS daily_stats ( total_revenue DECIMAL(15,2) DEFAULT 0.00, total_profit DECIMAL(15,2) DEFAULT 0.00, fuel_cost DECIMAL(15,2) DEFAULT 0.00, + maintenance_cost DECIMAL(15,2) DEFAULT 0.00, UNIQUE(user_id, date), FOREIGN KEY (user_id) REFERENCES users(id) ); +-- Kredite +CREATE TABLE IF NOT EXISTS loans ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + principal DECIMAL(15,2) NOT NULL, + current_amount DECIMAL(15,2) NOT NULL, + interest_rate DECIMAL(5,4) DEFAULT 0.02, + daily_interest DECIMAL(15,2) DEFAULT 0.00, + days_outstanding INTEGER DEFAULT 0, + status VARCHAR(20) DEFAULT 'active', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + due_date DATE, + FOREIGN KEY (user_id) REFERENCES users(id) +); + +-- Transaktionen (alle Buchungen) +CREATE TABLE IF NOT EXISTS transactions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + type VARCHAR(50) NOT NULL, + category VARCHAR(50) NOT NULL, + amount DECIMAL(15,2) NOT NULL, + balance_after DECIMAL(15,2) NOT NULL, + description TEXT DEFAULT '', + reference_id INTEGER, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) +); + +-- Wartungshistorie +CREATE TABLE IF NOT EXISTS maintenance_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + aircraft_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + maintenance_type VARCHAR(50) DEFAULT 'scheduled', + cost DECIMAL(15,2) NOT NULL, + condition_before DECIMAL(5,2) DEFAULT 100.00, + condition_after DECIMAL(5,2) DEFAULT 100.00, + notes TEXT DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (aircraft_id) REFERENCES aircraft(id), + FOREIGN KEY (user_id) REFERENCES users(id) +); + -- Events (Werbeaktionen, Wettbewerbe) CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -114,3 +159,27 @@ CREATE INDEX IF NOT EXISTS idx_flights_user ON flights(user_id); CREATE INDEX IF NOT EXISTS idx_flights_route ON flights(route_id); CREATE INDEX IF NOT EXISTS idx_aircraft_user ON aircraft(user_id); CREATE INDEX IF NOT EXISTS idx_airports_iata ON airports(iata_code); + +-- Flugzeugtypen (Referenztabelle) +CREATE TABLE IF NOT EXISTS aircraft_types ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code VARCHAR(20) UNIQUE NOT NULL, + name VARCHAR(100) NOT NULL, + manufacturer VARCHAR(50) NOT NULL, + range_km INTEGER NOT NULL, + capacity INTEGER NOT NULL, + buy_price DECIMAL(15,2) NOT NULL, + crew_cost_per_tick DECIMAL(10,2) NOT NULL, + fuel_per_km DECIMAL(6,4) DEFAULT 0.1500, + maintenance_interval INTEGER DEFAULT 50, + description TEXT DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +INSERT OR IGNORE INTO aircraft_types (code, name, manufacturer, range_km, capacity, buy_price, crew_cost_per_tick, fuel_per_km, maintenance_interval, description) VALUES +('dash8-100', 'Dash 8-100', 'Bombardier', 1500, 37, 500000.00, 200.00, 0.12, 50, 'Kleiner RegionalturboProp mit 37 Sitzen'), +('atr72-500', 'ATR 72-500', 'ATR', 1650, 70, 1500000.00, 300.00, 0.15, 50, 'Mittlerer RegionalturboProp mit 70 Sitzen'), +('a320-200', 'A320-200', 'Airbus', 5700, 150, 8000000.00, 600.00, 0.25, 50, 'Kurz-/Mittelstrecken-Jet mit 150 Sitzen'), +('b737-800', 'B737-800', 'Boeing', 5700, 162, 9500000.00, 650.00, 0.26, 50, 'Kurz-/Mittelstrecken-Jet mit 162 Sitzen'), +('b777-200', 'B777-200', 'Boeing', 12000, 300, 35000000.00, 1200.00, 0.55, 50, 'Langstrecken-Widebody mit 300 Sitzen'), +('a380-800', 'A380-800', 'Airbus', 15200, 525, 85000000.00, 2000.00, 0.80, 50, 'Doppelstöckiger Langstrecken-Airliner mit 525 Sitzen'); diff --git a/www/public/aircraft.php b/www/public/aircraft.php new file mode 100644 index 0000000..5d7521e --- /dev/null +++ b/www/public/aircraft.php @@ -0,0 +1,371 @@ +getUserById($userId); +$aircraft = $db->getUserAircraft($userId); +$config = $db->getConfig(); + +// Wartungsstatus prüfen +$maintenanceDue = $db->checkMaintenanceDue($userId); +?> + + + + + + Flotte - <?= htmlspecialchars($user['airline_name']) ?> - <?= $config['app']['name'] ?> + + + + + + + + +
+ +
+
+

🛫 Deine Flotte

+

Verwalte deine Flugzeuge, prüfe den Zustand und buche Wartungen

+
+
+ + + +
+
⚠️
+
+ Wartung fällig! +

Bei Flugzeug(en) ist die Wartung überfällig.

+
+
+ + + +
+
+
✈️
+
+
Flugzeuge
+
+
+
💺
+
+
Gesamtkapazität
+
+
+
🛡️
+
+
Wartung fällig
+
+
+
📅
+
+
Flüge bis Wartung
+
+
+ + +
+
+

🛩️ Alle Flugzeuge

+ + ➕ Flugzeug kaufen + +
+ + +
+
✈️
+

Keine Flugzeuge vorhanden

+

Du besitzt noch keine Flugzeuge. Kaufe dein erstes Flugzeug im Markt!

+ Zum Markt +
+ +
+ + + + + + + + + + + + + + + + = 80 ? 'text-success' : ($plane['condition'] >= 50 ? 'text-warning' : 'text-danger'); + $maintenanceProgress = min(100, ($plane['flights_count'] / $config['game']['maintenance_interval']) * 100); + $needsMaintenance = $plane['flights_count'] >= $config['game']['maintenance_interval']; + ?> + + + + + + + + + + + + +
NameTypZustandFlügeNächste WartungReichweiteKapazitätAktionen
+ + + + +
+ + % + +
+
+
+
+
+ + ⚠️ Jetzt! + + + Flüge + + + km Pax +
+ + + + +
+
+
+ +
+ + +
+

📊 Flugzeugtypen

+
+ $specs): ?> +
+

+
+
+ Preis: + +
+
+ Reichweite: + km +
+
+ Kapazität: + Pax +
+
+ Crew-Kosten: + €/Tick +
+
+
+ +
+
+
+ + + + + + + + +
+ + + + + diff --git a/www/public/api/game.php b/www/public/api/game.php index e05b3b1..5660caf 100644 --- a/www/public/api/game.php +++ b/www/public/api/game.php @@ -49,6 +49,37 @@ switch ($action) { handleLeaderboard(); break; + // NEU: Kredite + case 'take_loan': + handleTakeLoan(); + break; + + case 'repay_loan': + handleRepayLoan(); + break; + + case 'loans': + handleLoans(); + break; + + // NEU: Wartung + case 'maintenance': + handleMaintenance(); + break; + + case 'aircraft_details': + handleAircraftDetails(); + break; + + // NEU: Finanzen + case 'transactions': + handleTransactions(); + break; + + case 'finance_summary': + handleFinanceSummary(); + break; + default: jsonResponse(['error' => 'Unbekannte Aktion'], 400); } @@ -329,3 +360,216 @@ function handleLeaderboard() { 'count' => count($leaders) ]); } + +// ============================================ +// KREDIT HANDLERS +// ============================================ +function handleTakeLoan() { + global $db, $config; + + $userId = Session::getUserId(); + + // CSRF Check + if (!Session::validateCsrfToken($_POST['csrf_token'] ?? '')) { + jsonResponse(['error' => 'Ungültiges CSRF-Token'], 403); + } + + $amount = (float) ($_POST['amount'] ?? 0); + + if ($amount <= 0) { + jsonResponse(['error' => 'Ungültiger Betrag'], 400); + } + + try { + $loanId = $db->createLoan($userId, $amount); + + // Update user balance + $user = $db->getUserById($userId); + + jsonResponse([ + 'success' => true, + 'message' => 'Kredit über ' . number_format($amount, 0, ',', '.') . ' € aufgenommen!', + 'loan_id' => $loanId, + 'amount' => $amount, + 'new_balance' => $user['balance'] + $amount, + 'interest_rate' => $config['loans']['interest_rate'], + 'daily_interest' => $amount * $config['loans']['interest_rate'] + ]); + } catch (Exception $e) { + jsonResponse(['error' => $e->getMessage()], 400); + } +} + +function handleRepayLoan() { + global $db; + + $userId = Session::getUserId(); + + // CSRF Check + if (!Session::validateCsrfToken($_POST['csrf_token'] ?? '')) { + jsonResponse(['error' => 'Ungültiges CSRF-Token'], 403); + } + + $loanId = (int) ($_POST['loan_id'] ?? 0); + $amount = (float) ($_POST['amount'] ?? 0); + + if ($loanId <= 0 || $amount <= 0) { + jsonResponse(['error' => 'Ungültige Parameter'], 400); + } + + try { + $result = $db->repayLoan($userId, $loanId, $amount); + + jsonResponse([ + 'success' => true, + 'message' => number_format($result['repaid'], 2, ',', '.') . ' € zurückgezahlt!', + 'repaid' => $result['repaid'], + 'remaining' => $result['remaining'], + 'new_balance' => $result['new_balance'] + ]); + } catch (Exception $e) { + jsonResponse(['error' => $e->getMessage()], 400); + } +} + +function handleLoans() { + global $db; + + $userId = Session::getUserId(); + $loans = $db->getUserLoans($userId); + $totalDebt = $db->getTotalDebt($userId); + $totalDebtWithInterest = $db->getTotalDebtWithInterest($userId); + + jsonResponse([ + 'success' => true, + 'loans' => $loans, + 'total_debt' => $totalDebt, + 'total_debt_with_interest' => $totalDebtWithInterest, + 'count' => count($loans) + ]); +} + +// ============================================ +// WARTUNG HANDLERS +// ============================================ +function handleMaintenance() { + global $db, $config; + + $userId = Session::getUserId(); + + // CSRF Check + if (!Session::validateCsrfToken($_POST['csrf_token'] ?? '')) { + jsonResponse(['error' => 'Ungültiges CSRF-Token'], 403); + } + + $aircraftId = (int) ($_POST['aircraft_id'] ?? 0); + + if ($aircraftId <= 0) { + jsonResponse(['error' => 'Flugzeug-ID erforderlich'], 400); + } + + try { + $result = $db->performMaintenance($userId, $aircraftId); + + jsonResponse([ + 'success' => true, + 'message' => 'Wartung erfolgreich durchgeführt!', + 'cost' => $result['cost'], + 'condition_before' => $result['condition_before'], + 'condition_after' => $result['condition_after'], + 'new_balance' => $result['new_balance'] + ]); + } catch (Exception $e) { + jsonResponse(['error' => $e->getMessage()], 400); + } +} + +function handleAircraftDetails() { + global $db; + + $userId = Session::getUserId(); + $aircraftId = (int) ($_GET['id'] ?? 0); + + if ($aircraftId <= 0) { + jsonResponse(['error' => 'Flugzeug-ID erforderlich'], 400); + } + + $aircraft = $db->selectOne( + "SELECT * FROM aircraft WHERE id = ? AND user_id = ?", + [$aircraftId, $userId] + ); + + if (!$aircraft) { + jsonResponse(['error' => 'Flugzeug nicht gefunden'], 404); + } + + // Get maintenance history for this aircraft + $maintenanceHistory = $db->select( + "SELECT * FROM maintenance_history WHERE aircraft_id = ? ORDER BY created_at DESC LIMIT 10", + [$aircraftId] + ); + + // Get aircraft config for maintenance cost estimate + $config = $db->getConfig(); + $aircraftConfig = $config['aircraft_types'][$aircraft['aircraft_type']] ?? []; + $maintenanceCost = $aircraftConfig['maintenance_base_cost'] ?? 50000; + + // Adjust for condition + $conditionLoss = 100 - $aircraft['condition']; + $estimatedCost = $maintenanceCost + ($conditionLoss * 0.01 * $maintenanceCost); + + jsonResponse([ + 'success' => true, + 'aircraft' => $aircraft, + 'maintenance_history' => $maintenanceHistory, + 'maintenance_cost' => $estimatedCost, + 'maintenance_interval' => $config['game']['maintenance_interval'] + ]); +} + +// ============================================ +// FINANZEN HANDLERS +// ============================================ +function handleTransactions() { + global $db; + + $userId = Session::getUserId(); + $limit = min(100, (int) ($_GET['limit'] ?? 50)); + + $transactions = $db->getUserTransactions($userId, $limit); + + jsonResponse([ + 'success' => true, + 'transactions' => $transactions, + 'count' => count($transactions) + ]); +} + +function handleFinanceSummary() { + global $db; + + $userId = Session::getUserId(); + $user = $db->getUserById($userId); + + $today = date('Y-m-d'); + $weekAgo = date('Y-m-d', strtotime('-7 days')); + $monthAgo = date('Y-m-d', strtotime('-30 days')); + + $todayStats = $db->getTransactionSummary($userId, $today, $today); + $weekStats = $db->getTransactionSummary($userId, $weekAgo, $today); + $monthStats = $db->getTransactionSummary($userId, $monthAgo, $today); + + $loans = $db->getUserLoans($userId); + $totalDebt = $db->getTotalDebtWithInterest($userId); + + jsonResponse([ + 'success' => true, + 'balance' => $user['balance'], + 'net_worth' => $user['balance'] - $totalDebt, + 'total_debt' => $totalDebt, + 'today' => $todayStats, + 'week' => $weekStats, + 'month' => $monthStats, + 'active_loans' => count($loans) + ]); +} diff --git a/www/public/assets/css/style.css b/www/public/assets/css/style.css index 676b232..06e2c8c 100644 --- a/www/public/assets/css/style.css +++ b/www/public/assets/css/style.css @@ -1048,3 +1048,373 @@ a:hover { height: 48px; } } + +/* ============================================ + PHASE 2: AIRCRAFT, MARKET & FINANCES STYLES + ============================================ */ + +/* Aircraft Market Grid */ +.aircraft-market-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: var(--space-lg); +} + +.market-aircraft-card { + background: var(--bg-secondary); + border: 3px solid var(--text-primary); + border-radius: var(--radius-lg); + padding: var(--space-lg); + box-shadow: var(--shadow-md); + transition: transform var(--transition-fast); +} + +.market-aircraft-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); +} + +.market-aircraft-card.disabled { + opacity: 0.6; + filter: grayscale(30%); +} + +.market-aircraft-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--space-md); + padding-bottom: var(--space-md); + border-bottom: 2px dashed var(--border-color); +} + +.market-aircraft-name { + font-family: var(--font-primary); + font-size: 1.25rem; + color: var(--text-primary); +} + +.market-aircraft-price { + font-family: var(--font-primary); + font-size: 1.5rem; + font-weight: bold; +} + +.market-aircraft-specs { + display: flex; + flex-direction: column; + gap: var(--space-sm); +} + +.spec-row { + display: flex; + justify-content: space-between; + padding: var(--space-xs) 0; +} + +.spec-label { + color: var(--text-secondary); + font-size: 0.875rem; +} + +.spec-value { + font-weight: 600; + color: var(--text-primary); +} + +.cannot-afford { + text-align: center; + padding: var(--space-md); + background: var(--bg-tertiary); + border-radius: var(--radius-md); +} + +/* Aircraft Types Grid */ +.aircraft-types-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: var(--space-md); +} + +.aircraft-type-card { + background: var(--bg-tertiary); + border: 2px solid var(--border-color); + border-radius: var(--radius-md); + padding: var(--space-md); +} + +.aircraft-type-name { + font-family: var(--font-primary); + font-size: 1.1rem; + margin-bottom: var(--space-sm); + padding-bottom: var(--space-sm); + border-bottom: 1px dashed var(--border-color); +} + +.aircraft-type-stats { + display: flex; + flex-direction: column; + gap: var(--space-xs); +} + +.aircraft-type-stat { + display: flex; + justify-content: space-between; + font-size: 0.875rem; +} + +/* Balance Banner */ +.balance-banner { + display: flex; + justify-content: space-between; + align-items: center; + background: var(--accent-gradient); + color: white; + padding: var(--space-lg); +} + +.balance-info { + display: flex; + flex-direction: column; + gap: var(--space-xs); +} + +.balance-label { + font-size: 0.875rem; + opacity: 0.9; +} + +.balance-value { + font-family: var(--font-primary); + font-size: 2rem; + font-weight: bold; +} + +/* Period Stats */ +.period-stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: var(--space-md); +} + +.period-card { + background: var(--bg-tertiary); + border: 2px solid var(--border-color); + border-radius: var(--radius-md); + padding: var(--space-md); +} + +.period-card h3 { + font-family: var(--font-primary); + font-size: 1rem; + margin-bottom: var(--space-md); + padding-bottom: var(--space-sm); + border-bottom: 1px solid var(--border-color); +} + +.period-amounts { + display: flex; + flex-direction: column; + gap: var(--space-sm); +} + +.amount-row { + display: flex; + justify-content: space-between; + font-size: 0.875rem; +} + +.amount-row.total { + font-weight: bold; + padding-top: var(--space-sm); + border-top: 1px dashed var(--border-color); + margin-top: var(--space-sm); +} + +/* Loan Info */ +.loan-info { + background: var(--info-bg); + border: 2px solid var(--accent-color); + border-radius: var(--radius-md); + padding: var(--space-md); +} + +.loan-info h3 { + font-family: var(--font-primary); + margin-bottom: var(--space-md); +} + +.loan-info ul { + list-style: none; + padding: 0; + margin: 0; +} + +.loan-info li { + padding: var(--space-xs) 0; + border-bottom: 1px dashed var(--border-color); +} + +.loan-info li:last-child { + border-bottom: none; +} + +/* Aircraft Detail Modal */ +.aircraft-detail-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--space-lg); + padding-bottom: var(--space-md); + border-bottom: 2px dashed var(--border-color); +} + +.aircraft-detail-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--space-md); +} + +.detail-item { + display: flex; + flex-direction: column; + gap: var(--space-xs); +} + +.detail-label { + font-size: 0.75rem; + color: var(--text-secondary); + text-transform: uppercase; +} + +.detail-value { + font-weight: 600; + font-size: 1rem; +} + +.maintenance-history { + list-style: none; + padding: 0; + margin: var(--space-md) 0 0 0; +} + +.maintenance-history li { + padding: var(--space-sm); + background: var(--bg-tertiary); + border-radius: var(--radius-sm); + margin-bottom: var(--space-sm); + font-size: 0.875rem; +} + +/* Info Grid */ +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: var(--space-lg); +} + +.info-card { + background: var(--bg-secondary); + border: 2px solid var(--border-color); + border-radius: var(--radius-md); + padding: var(--space-md); +} + +.info-card h3 { + font-family: var(--font-primary); + font-size: 1rem; + margin-bottom: var(--space-md); +} + +.info-list { + list-style: disc; + padding-left: var(--space-lg); +} + +.info-list li { + padding: var(--space-xs) 0; + font-size: 0.875rem; + color: var(--text-secondary); +} + +/* Maintenance Progress */ +.maintenance-progress { + display: flex; + align-items: center; + gap: var(--space-sm); +} + +.maintenance-bar { + flex: 1; + height: 8px; + background: var(--bg-tertiary); + border-radius: 4px; + overflow: hidden; +} + +.maintenance-bar-fill { + height: 100%; + background: var(--comic-green); + transition: width var(--transition-normal); +} + +.maintenance-bar-fill.warning { + background: var(--comic-orange); +} + +.maintenance-bar-fill.danger { + background: var(--comic-red); +} + +/* D-flex utilities */ +.d-flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.align-start { + align-items: flex-start; +} + +.gap-sm { + gap: var(--space-sm); +} + +.gap-md { + gap: var(--space-md); +} + +.w-100 { + width: 100%; +} + +/* Responsive table container */ +.table-container { + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +/* Aircraft condition colors */ +.condition-excellent { + color: var(--comic-green); +} + +.condition-good { + color: var(--accent-color); +} + +.condition-warning { + color: var(--comic-orange); +} + +.condition-danger { + color: var(--comic-red); +} + +/* Aircraft table */ +.aircraft-table td { + vertical-align: middle; +} diff --git a/www/public/dashboard.php b/www/public/dashboard.php index 6de0944..7f69b59 100644 --- a/www/public/dashboard.php +++ b/www/public/dashboard.php @@ -24,6 +24,10 @@ $todayStats = $db->getTodayStats($userId); $aircraft = $db->getUserAircraft($userId); $routes = $db->getUserRoutes($userId); +// Kredite und Finanzen +$totalDebt = $db->getTotalDebtWithInterest($userId); +$maintenanceDue = $db->checkMaintenanceDue($userId); + // Level-Fortschritt berechnen $expForNextLevel = $config['game']['level_up_threshold'] * pow($config['game']['level_multiplier'], $user['level'] - 1); $levelProgress = ($user['experience'] / $expForNextLevel) * 100; @@ -47,9 +51,10 @@ $levelProgress = ($user['experience'] / $expForNextLevel) * 100;