Files
game1.shadow-land.de/www/public/api/game.php
madgerm 12ddf5a185 feat(phase2): aircraft market, fleet management, finances, loans
- 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
2026-04-24 17:27:02 +02:00

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)
]);
}