2026-04-15 23:50:01 +02:00

364 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MercyV Garage</title>
<link rel="stylesheet" href="style.css">
<script src="vue.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div id="app" v-show="show">
<!-- ══════════════════════════ GARAGE PANEL ══════════════════════════ -->
<div class="mv-backdrop-transparent" v-show="!showAdminPanel">
<div class="mv-modal">
<!-- HEADER -->
<div class="mv-header">
<div class="mv-header-left">
<div class="mv-header-icon">
<img src="images/logo/logo.png" class="mv-logo-img" alt="MercyV" onerror="this.style.display='none'">
</div>
<div class="mv-header-title">
<span class="mv-title-main">MercyV Garage</span>
<span class="mv-title-sub">{{ garageId || 'Garage' }}</span>
</div>
</div>
<div class="mv-header-right">
<div class="mv-player-name">{{ playerName }}</div>
<button class="mv-close-btn" @click="close()"><i class="fas fa-times"></i></button>
</div>
</div>
<!-- BODY -->
<div class="mv-body">
<!-- SIDEBAR -->
<div class="mv-sidebar">
<div class="mv-sidebar-item" :class="activeTab==='all'?'active':''" @click="setTab('all')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'||garageType==='impoundboat'||garageType==='impoundplane'">
<i class="fas fa-border-all"></i><span>Alle</span>
</div>
<div class="mv-sidebar-item" :class="activeTab==='cars'?'active':''" @click="setTab('cars')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'">
<i class="fas fa-car"></i><span>Autos</span>
</div>
<div class="mv-sidebar-item" :class="activeTab==='motor'?'active':''" @click="setTab('motor')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'">
<i class="fas fa-motorcycle"></i><span>Moto</span>
</div>
<div class="mv-sidebar-item" :class="activeTab==='bikes'?'active':''" @click="setTab('bikes')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'">
<i class="fas fa-bicycle"></i><span>Bikes</span>
</div>
<div class="mv-sidebar-item" :class="activeTab==='boat'?'active':''" @click="setTab('boat')" v-show="garageType==='boat'||garageType==='impoundboat'">
<i class="fas fa-ship"></i><span>Boote</span>
</div>
<div class="mv-sidebar-item" :class="activeTab==='aircraft'?'active':''" @click="setTab('aircraft')" v-show="garageType==='aircraft'||garageType==='impoundplane'">
<i class="fas fa-plane"></i><span>Flug</span>
</div>
<div class="mv-sidebar-divider"></div>
<div class="mv-sidebar-item" :class="showFavorites?'active-fav':''" @click="toggleFavorites()" v-show="garageType!=='jobgarage'">
<i :class="showFavorites?'fas fa-star':'far fa-star'"></i><span>Favoriten</span>
</div>
<div class="mv-sidebar-spacer"></div>
<div class="mv-sidebar-item mv-admin-item" v-if="isAdmin" @click="openAdminFromGarage()">
<i class="fas fa-cog"></i><span>Admin</span>
</div>
</div>
<!-- MAIN -->
<div class="mv-main">
<!-- TOPBAR -->
<div class="mv-topbar">
<div class="mv-search-wrap">
<i class="fas fa-search mv-search-icon"></i>
<input v-model="searchQuery" class="mv-search" placeholder="Fahrzeug suchen...">
</div>
<div class="mv-impound-badge" v-if="garageType==='impound'||garageType==='impoundboat'||garageType==='impoundplane'">
<i class="fas fa-euro-sign"></i> Abholgebühr: <strong>{{ impoundPrice }}$</strong>
</div>
</div>
<!-- CONTENT ROW: Grid + Detail -->
<div class="mv-content-row">
<!-- GRID -->
<div class="mv-grid">
<div class="mv-card" v-for="(v, i) in filteredVehicles" :key="v.plate"
@click="selectVehicle(v, i)"
:class="selectedIndex===i ? 'mv-card-active' : ''">
<div class="mv-card-status" :class="v.nearby ? 'status-nearby' : v.stored===0 ? 'status-out' : 'status-in'">
{{ v.nearby ? 'In der Nähe' : v.stored===0 ? 'Draußen' : 'Eingelagert' }}
</div>
<img :src="getVehicleImage(v.modelname, v.carimage)" class="mv-card-img" onerror="this.src='images/defaultimage.png'">
<div class="mv-card-name">{{ v.carname }}</div>
<div class="mv-card-plate">{{ v.plate }}</div>
<div class="mv-card-fav" @click.stop="toggleFav(v.plate, v.favorite)">
<i :class="v.favorite===1?'fas fa-star':'far fa-star'"></i>
</div>
</div>
<div class="mv-empty" v-if="filteredVehicles.length===0">
<i class="fas fa-car-side"></i>
<p>Keine Fahrzeuge</p>
</div>
</div>
<!-- DETAIL -->
<div class="mv-detail" v-if="selectedVehicle">
<div class="mv-detail-img-wrap">
<img :src="getVehicleImage(selectedVehicle.modelname, selectedVehicle.carimage)" class="mv-detail-img" onerror="this.src='images/defaultimage.png'">
</div>
<div class="mv-detail-name">{{ selectedVehicle.carname }}</div>
<div class="mv-detail-plate"><i class="fas fa-id-card"></i> {{ selectedVehicle.plate }}</div>
<div class="mv-detail-garage" v-if="selectedVehicle.parking"><i class="fas fa-warehouse"></i> {{ selectedVehicle.parking }}</div>
<div class="mv-detail-status" :class="selectedVehicle.stored===0?'ds-out':'ds-in'">
<i :class="selectedVehicle.stored===0?'fas fa-map-marker-alt':'fas fa-lock'"></i>
{{ selectedVehicle.stored===0 ? 'Aktuell draußen' : 'Eingelagert' }}
</div>
<button class="mv-takeout-btn" @click="takeOut()" v-if="selectedVehicle.stored===1">
<i class="fas fa-car"></i> Fahrzeug holen
</button>
<button class="mv-parkin-btn" @click="parkFromPanel()" v-if="selectedVehicle.nearby && selectedVehicle.stored===0">
<i class="fas fa-parking"></i> Fahrzeug einparken
</button>
<div class="mv-already-out" v-if="!selectedVehicle.nearby && selectedVehicle.stored===0">
<i class="fas fa-map-marker-alt"></i> Fahrzeug ist draußen
</div>
<div class="mv-detail-fav" @click="toggleFav(selectedVehicle.plate, selectedVehicle.favorite)">
<i :class="selectedVehicle.favorite===1?'fas fa-star':'far fa-star'"></i>
{{ selectedVehicle.favorite===1 ? 'Favorit entfernen' : 'Zu Favoriten' }}
</div>
</div>
</div><!-- /.mv-content-row -->
</div><!-- /.mv-main -->
</div><!-- /.mv-body -->
</div><!-- /.mv-modal -->
</div><!-- garage panel -->
<!-- ══════════════════════════ ADMIN PANEL ══════════════════════════ -->
<div class="mv-backdrop-transparent" v-if="showAdminPanel">
<div class="mv-modal mv-modal-wide">
<div class="mv-header">
<div class="mv-header-left">
<div class="mv-header-icon">
<i class="fas fa-cog" style="color:#E8830A;font-size:14px;"></i>
</div>
<div class="mv-header-title">
<span class="mv-title-main">Garage Editor</span>
<span class="mv-title-sub">Admin Tool</span>
</div>
</div>
<div class="mv-header-right">
<button class="mv-btn-orange" @click="newGarage()"><i class="fas fa-plus"></i> Neue Garage</button>
<button class="mv-close-btn" @click="closeAdmin()"><i class="fas fa-times"></i></button>
</div>
</div>
<div class="mv-body">
<div class="mv-sidebar mv-admin-sidebar">
<div style="padding:10px 12px 6px;font-size:11px;color:rgba(255,255,255,0.3);letter-spacing:0.1em;text-transform:uppercase;">
Garagen ({{ adminGarages.length }})
</div>
<div class="mv-admin-list">
<div class="mv-admin-list-item"
v-for="g in adminGarages" :key="g.id"
:class="editingGarage && editingGarage.id===g.id ? 'admin-item-active':''"
@click="editGarage(g)">
<div class="mv-admin-item-icon" :class="'type-icon-'+g.type">
<i :class="getTypeIcon(g.type)"></i>
</div>
<div class="mv-admin-item-info">
<div class="mv-admin-item-label">{{ g.label }}</div>
<div class="mv-admin-item-type">{{ g.type }}</div>
</div>
<div class="mv-admin-item-btns">
<button class="mv-icon-btn mv-icon-btn-blue" @click.stop="teleportTo(g)"><i class="fas fa-location-arrow"></i></button>
<button class="mv-icon-btn mv-icon-btn-red" @click.stop="confirmDelete(g.id)"><i class="fas fa-trash"></i></button>
</div>
</div>
</div>
</div>
<div class="mv-main mv-admin-main" v-if="editingGarage">
<div class="mv-topbar" style="border-bottom:1px solid rgba(255,255,255,0.06);">
<div style="font-size:14px;font-weight:600;color:#fff;">
{{ editingGarage._isNew ? 'Neue Garage erstellen' : 'Bearbeiten: ' + editingGarage.label }}
</div>
<button class="mv-btn-orange" @click="saveGarage()"><i class="fas fa-save"></i> Speichern</button>
</div>
<div class="mv-form-scroll">
<div class="mv-form-section">
<div class="mv-form-section-title"><i class="fas fa-info-circle"></i> Basis</div>
<div class="mv-form-grid">
<div class="mv-form-field">
<label>ID</label>
<input v-model="editingGarage.id" :disabled="!editingGarage._isNew" placeholder="garage_ls_01">
</div>
<div class="mv-form-field">
<label>Anzeigename</label>
<input v-model="editingGarage.label" placeholder="Garage LS">
</div>
<div class="mv-form-field">
<label>Typ</label>
<select v-model="editingGarage.type">
<option value="normal">Normal</option>
<option value="aircraft">Flugzeuge</option>
<option value="boat">Boote</option>
<option value="jobgarage">Job-Garage</option>
<option value="impound">Abschlepphof (Auto)</option>
<option value="impoundboat">Abschlepphof (Boot)</option>
<option value="impoundplane">Abschlepphof (Flugzeug)</option>
</select>
</div>
<div class="mv-form-field">
<label>Job-Zugang</label>
<input v-model="editingGarage.access" placeholder="none">
</div>
<div class="mv-form-field">
<label>NPC Model</label>
<input v-model="editingGarage.npc_model" placeholder="a_m_m_prolhost_01">
</div>
<div class="mv-form-field mv-form-field-check">
<label>Blip anzeigen</label>
<input type="checkbox" v-model="editingGarage.blip_show_bool">
</div>
</div>
</div>
<div class="mv-form-section">
<div class="mv-form-section-title">
<i class="fas fa-map-marker-alt"></i> Positionen
<span class="mv-hint-pill"><i class="fas fa-info-circle"></i> Position aufsuchen → "Hier erfassen" → E drücken</span>
</div>
<div class="mv-pos-block" v-for="pos in posFields" :key="pos.field">
<div class="mv-pos-label">{{ pos.label }}</div>
<div class="mv-pos-row">
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_x']" placeholder="X">
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_y']" placeholder="Y">
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_z']" placeholder="Z">
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_heading']" placeholder="Heading" v-if="pos.hasHeading">
</div>
<button class="mv-capture-btn" @click="capturePos(pos.field)">
<i class="fas fa-crosshairs"></i> Hier erfassen
</button>
</div>
</div>
<!-- JOB-FAHRZEUGE: nur bei Jobgaragen -->
<div class="mv-form-section" v-if="editingGarage.type === 'jobgarage'">
<div class="mv-form-section-title">
<i class="fas fa-car"></i> Job-Fahrzeuge
<span class="mv-hint-pill"><i class="fas fa-info-circle"></i> GTA-Modellname z.B. police3, ambulance</span>
</div>
<div class="mv-job-vehicle-list">
<div class="mv-job-vehicle-item" v-for="(v, i) in jobVehicles" :key="i">
<span class="mv-job-vehicle-name"><i class="fas fa-car-side"></i> {{ v.model }}</span>
<button class="mv-icon-btn mv-icon-btn-red" @click="removeJobVehicle(i)">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="mv-job-vehicle-empty" v-if="jobVehicles.length === 0">
Noch keine Fahrzeuge eingetragen
</div>
</div>
<div class="mv-job-vehicle-add">
<input v-model="newJobVehicle"
placeholder="Modellname (z.B. police3)"
@keyup.enter="addJobVehicle()"
class="mv-job-input">
<button class="mv-btn-orange" @click="addJobVehicle()">
<i class="fas fa-plus"></i> Hinzufügen
</button>
</div>
</div>
</div><!-- /.mv-form-scroll -->
</div>
<div class="mv-admin-empty" v-else>
<i class="fas fa-warehouse"></i>
<p>Garage auswählen oder neue erstellen</p>
</div>
</div><!-- /.mv-body -->
</div><!-- /.mv-modal-wide -->
</div><!-- admin panel -->
<!-- DELETE CONFIRM -->
<div class="mv-confirm-overlay" v-if="deleteConfirmId">
<div class="mv-confirm-box">
<i class="fas fa-exclamation-triangle mv-confirm-icon"></i>
<p class="mv-confirm-title">Garage löschen?</p>
<p class="mv-confirm-sub"><strong>{{ deleteConfirmId }}</strong> wird permanent entfernt.</p>
<div class="mv-confirm-btns">
<button class="mv-btn-danger" @click="deleteGarage()"><i class="fas fa-trash"></i> Löschen</button>
<button class="mv-btn-ghost" @click="deleteConfirmId=null">Abbrechen</button>
</div>
</div>
</div>
<!-- CAPTURE OVERLAY -->
<div class="mv-capture-overlay" v-if="capturingField">
<div class="mv-capture-box">
<i class="fas fa-crosshairs mv-capture-icon"></i>
<p class="mv-capture-title">Position erfassen</p>
<p class="mv-capture-field">{{ getFieldLabel(capturingField) }}</p>
<p class="mv-capture-hint">Geh zur Position → <kbd>E</kbd> drücken</p>
<p class="mv-capture-cancel"><kbd>ESC</kbd> = Abbrechen</p>
</div>
</div>
</div><!-- /#app -->
<script>
// ═══════════════════════════════════════════════════════════════
// MercyV Garage Fahrzeugbilder Konfiguration
// Modellname → Bild-URL (lokal oder extern)
//
// Beispiele:
// 'police3': 'images/cars/police3.png' (lokale Datei)
// 'ambulance': 'https://example.com/amb.png' (externe URL)
// 'gbpolbisonhf': 'images/cars/gbpolbisonhf.png'
// ═══════════════════════════════════════════════════════════════
const VehicleImages = {
// ── Polizei ───────────────────────────────────────────────
'police': 'images/cars/police.png',
'police2': 'images/cars/police2.png',
'police3': 'images/cars/police3.png',
'police4': 'images/cars/police4.png',
'gbpolbisonhf': 'images/cars/gbpolbisonhf.png',
'gbpolbisonstx': 'images/cars/gbpolbisonstx.png',
// ── Ambulanz ──────────────────────────────────────────────
'ambulance': 'images/cars/ambulance.png',
'frogger': 'images/cars/frogger.png',
// ── Eigene Fahrzeuge (hier ergänzen) ──────────────────────
// 'meinauto': 'images/cars/meinauto.png',
};
// Standardbild wenn kein Eintrag gefunden
const DefaultVehicleImage = 'images/defaultimage.png';
/**
* Gibt die Bild-URL für ein Fahrzeugmodell zurück.
* Sucht zuerst nach dem Modellnamen, dann nach dem Anzeigenamen (displayName).
*/
function getVehicleImage(modelname, displayName) {
const model = (modelname || '').toLowerCase().trim();
const display = (displayName || '').toLowerCase().trim();
return VehicleImages[model]
|| VehicleImages[display]
|| DefaultVehicleImage;
}
</script>
<script src="script.js"></script>
</body>
</html>