- market.php: Buy new aircraft (6 types: Dash8 to A380) - aircraft.php: Fleet management, crew assignment, route planning - finances.php: Account balance, transactions, loans - Database.php: Extended with loan/transaction/aircraft methods - schema.sql: Aircraft types table, 10M starting balance, INSERT OR IGNORE - dashboard.php: Updated with fleet/financial overview - Fixed: duplicate function declarations in Database.php - Fixed: initializeSchema() with idempotent error handling
576 lines
16 KiB
PHP
576 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* Airline Tycoon - Game API
|
|
* Hauptspiel-API für Flüge, Routen, Flugzeuge, etc.
|
|
*/
|
|
|
|
require_once dirname(__DIR__) . '/src/bootstrap.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// Auth Check für alle Aktionen außer 'status'
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? '';
|
|
|
|
$publicActions = ['status', 'info'];
|
|
if (!in_array($action, $publicActions) && !Session::isLoggedIn()) {
|
|
jsonResponse(['error' => 'Nicht angemeldet'], 401);
|
|
}
|
|
|
|
switch ($action) {
|
|
case 'status':
|
|
handleStatus();
|
|
break;
|
|
|
|
case 'info':
|
|
handleInfo();
|
|
break;
|
|
|
|
case 'purchase':
|
|
handlePurchase();
|
|
break;
|
|
|
|
case 'flight':
|
|
handleFlight();
|
|
break;
|
|
|
|
case 'routes':
|
|
handleRoutes();
|
|
break;
|
|
|
|
case 'create_route':
|
|
handleCreateRoute();
|
|
break;
|
|
|
|
case 'stats':
|
|
handleStats();
|
|
break;
|
|
|
|
case 'leaderboard':
|
|
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);
|
|
}
|
|
|
|
function handleStatus() {
|
|
global $db, $config;
|
|
|
|
$response = [
|
|
'success' => true,
|
|
'authenticated' => Session::isLoggedIn(),
|
|
'timestamp' => time(),
|
|
'config' => [
|
|
'start_balance' => $config['game']['start_balance'],
|
|
'fuel_cost_per_km' => $config['game']['fuel_cost_per_km']
|
|
]
|
|
];
|
|
|
|
if (Session::isLoggedIn()) {
|
|
$userId = Session::getUserId();
|
|
$user = $db->getUserById($userId);
|
|
|
|
if ($user) {
|
|
$response['user'] = [
|
|
'id' => $user['id'],
|
|
'username' => $user['username'],
|
|
'airline_name' => $user['airline_name'],
|
|
'balance' => $user['balance'],
|
|
'level' => $user['level'],
|
|
'experience' => $user['experience']
|
|
];
|
|
}
|
|
}
|
|
|
|
jsonResponse($response);
|
|
}
|
|
|
|
function handleInfo() {
|
|
global $config;
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'app' => $config['app'],
|
|
'game' => $config['game'],
|
|
'aircraft_types' => $config['aircraft_types']
|
|
]);
|
|
}
|
|
|
|
function handlePurchase() {
|
|
global $db, $config;
|
|
|
|
$userId = Session::getUserId();
|
|
$user = $db->getUserById($userId);
|
|
|
|
$type = $_POST['aircraft_type'] ?? '';
|
|
$name = trim($_POST['name'] ?? '');
|
|
|
|
if (empty($type) || !isset($config['aircraft_types'][$type])) {
|
|
jsonResponse(['error' => 'Ungültiger Flugzeugtyp'], 400);
|
|
}
|
|
|
|
$specs = $config['aircraft_types'][$type];
|
|
$price = $specs['purchase_price'];
|
|
|
|
if ($user['balance'] < $price) {
|
|
jsonResponse([
|
|
'error' => 'Nicht genug Geld!',
|
|
'required' => $price,
|
|
'available' => $user['balance']
|
|
], 400);
|
|
}
|
|
|
|
// Balance abziehen
|
|
$newBalance = $user['balance'] - $price;
|
|
$db->update('users', ['balance' => $newBalance], 'id = ?', [$userId]);
|
|
|
|
// Aircraft erstellen
|
|
$aircraftId = $db->purchaseAircraft($userId, $type, $name, $price);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'message' => "{$type} \"{$name}\" wurde gekauft!",
|
|
'aircraft_id' => $aircraftId,
|
|
'price' => $price,
|
|
'new_balance' => $newBalance
|
|
]);
|
|
}
|
|
|
|
function handleFlight() {
|
|
global $db;
|
|
|
|
$userId = Session::getUserId();
|
|
$user = $db->getUserById($userId);
|
|
|
|
$routeId = (int) ($_POST['route_id'] ?? 0);
|
|
$aircraftId = (int) ($_POST['aircraft_id'] ?? 0);
|
|
|
|
if ($routeId <= 0 || $aircraftId <= 0) {
|
|
jsonResponse(['error' => 'Route und Flugzeug erforderlich'], 400);
|
|
}
|
|
|
|
// Route prüfen
|
|
$route = $db->selectOne(
|
|
"SELECT r.*, a1.name as from_name, a2.name as to_name
|
|
FROM routes r
|
|
JOIN airports a1 ON r.from_airport_id = a1.id
|
|
JOIN airports a2 ON r.to_airport_id = a2.id
|
|
WHERE r.id = ? AND r.user_id = ? AND r.is_active = 1",
|
|
[$routeId, $userId]
|
|
);
|
|
|
|
if (!$route) {
|
|
jsonResponse(['error' => 'Route nicht gefunden'], 404);
|
|
}
|
|
|
|
// Aircraft prüfen
|
|
$aircraft = $db->selectOne(
|
|
"SELECT * FROM aircraft WHERE id = ? AND user_id = ?",
|
|
[$aircraftId, $userId]
|
|
);
|
|
|
|
if (!$aircraft) {
|
|
jsonResponse(['error' => 'Flugzeug nicht gefunden'], 404);
|
|
}
|
|
|
|
if ($aircraft['condition'] <= 0) {
|
|
jsonResponse(['error' => 'Flugzeug ist zu beschädigt!'], 400);
|
|
}
|
|
|
|
if ($aircraft['range_km'] < $route['distance_km']) {
|
|
jsonResponse([
|
|
'error' => 'Flugzeug hat nicht genug Reichweite!',
|
|
'required' => $route['distance_km'],
|
|
'available' => $aircraft['range_km']
|
|
], 400);
|
|
}
|
|
|
|
try {
|
|
$result = $db->executeFlight($userId, $routeId, $aircraftId);
|
|
|
|
// User-Daten aktualisieren
|
|
$updatedUser = $db->getUserById($userId);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'message' => 'Flug erfolgreich!',
|
|
'flight' => [
|
|
'from' => $route['from_name'],
|
|
'to' => $route['to_name'],
|
|
'passengers' => $result['passengers'],
|
|
'revenue' => $result['revenue'],
|
|
'fuel_cost' => $result['fuel_cost'],
|
|
'profit' => $result['profit']
|
|
],
|
|
'user' => [
|
|
'balance' => $updatedUser['balance'],
|
|
'experience' => $updatedUser['experience'],
|
|
'level' => $updatedUser['level']
|
|
]
|
|
]);
|
|
} catch (Exception $e) {
|
|
jsonResponse(['error' => 'Flug fehlgeschlagen: ' . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
function handleRoutes() {
|
|
global $db;
|
|
|
|
$userId = Session::getUserId();
|
|
$routes = $db->getUserRoutes($userId);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'routes' => $routes,
|
|
'count' => count($routes)
|
|
]);
|
|
}
|
|
|
|
function handleCreateRoute() {
|
|
global $db;
|
|
|
|
$userId = Session::getUserId();
|
|
|
|
$fromId = (int) ($_POST['from_airport'] ?? 0);
|
|
$toId = (int) ($_POST['to_airport'] ?? 0);
|
|
|
|
if ($fromId <= 0 || $toId <= 0) {
|
|
jsonResponse(['error' => 'Gültige Flughäfen erforderlich'], 400);
|
|
}
|
|
|
|
if ($fromId === $toId) {
|
|
jsonResponse(['error' => 'Von und Nach müssen unterschiedlich sein'], 400);
|
|
}
|
|
|
|
// Flughäfen prüfen
|
|
$fromAirport = $db->selectOne("SELECT * FROM airports WHERE id = ?", [$fromId]);
|
|
$toAirport = $db->selectOne("SELECT * FROM airports WHERE id = ?", [$toId]);
|
|
|
|
if (!$fromAirport || !$toAirport) {
|
|
jsonResponse(['error' => 'Ein oder beide Flughäfen nicht gefunden'], 404);
|
|
}
|
|
|
|
// Prüfen ob Route bereits existiert
|
|
$existing = $db->selectOne(
|
|
"SELECT id FROM routes WHERE user_id = ? AND from_airport_id = ? AND to_airport_id = ?",
|
|
[$userId, $fromId, $toId]
|
|
);
|
|
|
|
if ($existing) {
|
|
jsonResponse(['error' => 'Diese Route existiert bereits'], 409);
|
|
}
|
|
|
|
// Distance berechnen
|
|
$distance = $db->calculateDistance(
|
|
$fromAirport['latitude'], $fromAirport['longitude'],
|
|
$toAirport['latitude'], $toAirport['longitude']
|
|
);
|
|
|
|
// Route erstellen
|
|
$routeId = $db->createRoute($userId, $fromId, $toId, $distance);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'message' => "Route {$fromAirport['iata_code']} → {$toAirport['iata_code']} erstellt!",
|
|
'route_id' => $routeId,
|
|
'distance' => $distance
|
|
]);
|
|
}
|
|
|
|
function handleStats() {
|
|
global $db;
|
|
|
|
$userId = Session::getUserId();
|
|
$user = $db->getUserById($userId);
|
|
|
|
$stats = $db->getUserStats($userId);
|
|
$todayStats = $db->getTodayStats($userId);
|
|
$aircraft = $db->getUserAircraft($userId);
|
|
$routes = $db->getUserRoutes($userId);
|
|
|
|
// Level-Berechnung
|
|
$config = require dirname(__DIR__) . '/src/config.php';
|
|
$expForNextLevel = $config['game']['level_up_threshold'] * pow($config['game']['level_multiplier'], $user['level'] - 1);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'user' => [
|
|
'balance' => $user['balance'],
|
|
'level' => $user['level'],
|
|
'experience' => $user['experience'],
|
|
'exp_for_next' => $expForNextLevel
|
|
],
|
|
'stats' => $stats,
|
|
'today' => $todayStats,
|
|
'fleet_count' => count($aircraft),
|
|
'routes_count' => count($routes)
|
|
]);
|
|
}
|
|
|
|
function handleLeaderboard() {
|
|
global $db;
|
|
|
|
$limit = min(50, (int) ($_GET['limit'] ?? 10));
|
|
|
|
$leaders = $db->select(
|
|
"SELECT id, username, airline_name, balance, level, experience,
|
|
(SELECT COUNT(*) FROM routes WHERE user_id = users.id AND is_active = 1) as routes_count,
|
|
(SELECT COUNT(*) FROM flights WHERE user_id = users.id) as total_flights
|
|
FROM users
|
|
WHERE is_active = 1
|
|
ORDER BY balance DESC, level DESC, experience DESC
|
|
LIMIT ?",
|
|
[$limit]
|
|
);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'leaderboard' => $leaders,
|
|
'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)
|
|
]);
|
|
}
|