Sektor-Frontend Modularisierung
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
<div id="pixi-container"></div>
|
<div id="pixi-container"></div>
|
||||||
|
|
||||||
<div id="ui-layer">
|
<div id="ui-layer">
|
||||||
|
<div id="ui-safe-zone">
|
||||||
|
|
||||||
<div id="sector-top-bar">
|
<div id="sector-top-bar">
|
||||||
<div class="top-bar-left">
|
<div class="top-bar-left">
|
||||||
@@ -29,6 +30,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="resource-header">
|
<div id="resource-header">
|
||||||
|
<div id="logo-anchor"></div>
|
||||||
|
|
||||||
<div id="resource-bar">
|
<div id="resource-bar">
|
||||||
<div class="res-col" id="wrapper_res_primary" title="Lädt...">
|
<div class="res-col" id="wrapper_res_primary" title="Lädt...">
|
||||||
<div class="res-box" id="box_primary"><span class="res-icon" id="icon_primary"></span></div>
|
<div class="res-box" id="box_primary"><span class="res-icon" id="icon_primary"></span></div>
|
||||||
@@ -73,17 +76,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav id="side-menu-container">
|
<nav id="side-menu-container"></nav>
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="planet-limit-container">
|
<div id="planet-limit-container">
|
||||||
<span class="planet-limit-text">Planeten: <span id="val_planet_count">10</span> / <span id="val_planet_max">12</span></span>
|
<span class="planet-limit-text">Planeten: <span id="val_planet_count">10</span> / <span id="val_planet_max">12</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside id="right-sidebar-container">
|
<aside id="right-sidebar-container"></aside>
|
||||||
</aside>
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer id="game-footer">
|
<footer id="game-footer">
|
||||||
|
<div class="footer-safe-area">
|
||||||
<div class="footer-left">v 0.0.1.0-dev (Sector)</div>
|
<div class="footer-left">v 0.0.1.0-dev (Sector)</div>
|
||||||
<div class="footer-center">© 2026 Void-Genesis</div>
|
<div class="footer-center">© 2026 Void-Genesis</div>
|
||||||
<div class="footer-right">
|
<div class="footer-right">
|
||||||
@@ -93,6 +97,7 @@
|
|||||||
<a href="https://lobby.void-genesis.de/agb.html" target="_blank">AGB</a> |
|
<a href="https://lobby.void-genesis.de/agb.html" target="_blank">AGB</a> |
|
||||||
<a href="https://lobby.void-genesis.de/regeln.html" target="_blank">Spielregeln</a>
|
<a href="https://lobby.void-genesis.de/regeln.html" target="_blank">Spielregeln</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#game-footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 1200px;
|
||||||
|
height: 40px;
|
||||||
|
background: var(--color-panel-bg);
|
||||||
|
border-top: 1px solid var(--color-cyan-dark);
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-safe-area {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 1200px;
|
||||||
|
max-width: 1600px;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: var(--color-text-inactive);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#game-footer a {
|
||||||
|
color: var(--color-text-inactive);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease, text-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#game-footer a:hover {
|
||||||
|
color: var(--color-cyan-bright);
|
||||||
|
text-shadow: 0 0 5px var(--color-cyan-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-left { flex: 1; text-align: left; }
|
||||||
|
.footer-center { flex: 1; text-align: center; }
|
||||||
|
.footer-right { flex: 1; text-align: right; }
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Initialisiert die Footer-Komponente.
|
||||||
|
* Hier binden wir später Event-Listener für die externen Links
|
||||||
|
* oder rendern dynamisch die aktuelle Spielversion aus einer Config.
|
||||||
|
*/
|
||||||
|
export function initFooter() {
|
||||||
|
const footerElement = document.getElementById('game-footer');
|
||||||
|
|
||||||
|
if (!footerElement) {
|
||||||
|
console.warn('[Footer] Konnte #game-footer nicht im DOM finden.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('[Footer] Erfolgreich initialisiert.');
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#planet-limit-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 150px;
|
||||||
|
right: 20px;
|
||||||
|
width: 215px;
|
||||||
|
height: 45px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 20;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-limit-text {
|
||||||
|
color: var(--color-text-main);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#val_planet_count {
|
||||||
|
color: var(--color-cyan-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
#val_planet_max {
|
||||||
|
color: var(--color-text-inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
#right-sidebar-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 220px;
|
||||||
|
right: 20px;
|
||||||
|
width: 215px;
|
||||||
|
|
||||||
|
bottom: 60px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 25px;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 20;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 30px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-item {
|
||||||
|
position: relative;
|
||||||
|
width: 200px;
|
||||||
|
height: 90px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: flex-start;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-click-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moon-click-area {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moon-click-area.has-moon {
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: 1px solid rgba(0, 240, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-icon-anchor {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moon-icon-anchor {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
z-index: 25;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moon-click-area:hover .moon-icon-anchor {
|
||||||
|
transform: scale(1.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-text-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-name {
|
||||||
|
color: var(--color-text-main);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
transition: color 0.2s ease, text-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planet-coords {
|
||||||
|
color: var(--color-text-inactive);
|
||||||
|
font-size: 11px;
|
||||||
|
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right-sidebar-container::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right-sidebar-container::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right-sidebar-container::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-cyan-dark);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right-sidebar-container::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--color-cyan-bright);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// PLANETEN-KONFIGURATION (Dummy-Daten)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Das aktualisierte datengetriebene Array für unsere Planetenleiste.
|
||||||
|
*
|
||||||
|
* Eigenschaften:
|
||||||
|
* - id: Eindeutige HTML-ID für die PixiJS-Synchronisation
|
||||||
|
* - name: Der Anzeigename der Welt (Alpha, Beta, etc.)
|
||||||
|
* - coords: Die Koordinaten
|
||||||
|
* - isActive: Boolean; markiert den aktuell ausgewählten Planeten
|
||||||
|
* - hasMoon: Boolean; steuert, ob das kleine Mond-Icon gerendert werden soll
|
||||||
|
* - planetColor: Hex-Wert; damit färben wir später das Planet-Icon (tint) dynamisch ein
|
||||||
|
*/
|
||||||
|
export const planetConfig = [
|
||||||
|
{
|
||||||
|
id: 'planet_btn_alpha',
|
||||||
|
name: 'Alpha',
|
||||||
|
coords: '1:15:6',
|
||||||
|
isActive: true,
|
||||||
|
hasMoon: true,
|
||||||
|
planetColor: 0x40a0b0 // Dezentere Cyan-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_beta',
|
||||||
|
name: 'Beta',
|
||||||
|
coords: '1:15:8',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: false,
|
||||||
|
planetColor: 0xc08040 // Dezentere Orange/Amber-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_gamma',
|
||||||
|
name: 'Gamma',
|
||||||
|
coords: '1:16:7',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: true,
|
||||||
|
planetColor: 0x40a050 // Dezentere Salbei-Grün-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_delta',
|
||||||
|
name: 'Delta',
|
||||||
|
coords: '1:16:8',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: false,
|
||||||
|
planetColor: 0xa050a0 // Dezentere Violett-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_epsilon',
|
||||||
|
name: 'Epsilon',
|
||||||
|
coords: '1:16:9',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: false,
|
||||||
|
planetColor: 0xc08040 // Dezentere Orange/Amber-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_zeta',
|
||||||
|
name: 'Zeta',
|
||||||
|
coords: '1:17:10',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: true,
|
||||||
|
planetColor: 0x40a050 // Dezentere Salbei-Grün-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_eta',
|
||||||
|
name: 'Eta',
|
||||||
|
coords: '1:17:12',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: false,
|
||||||
|
planetColor: 0xa050a0 // Dezentere Violett-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_theta',
|
||||||
|
name: 'Theta',
|
||||||
|
coords: '1:20:4',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: false,
|
||||||
|
planetColor: 0xc08040 // Dezentere Orange/Amber-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_iota',
|
||||||
|
name: 'Iota',
|
||||||
|
coords: '1:20:10',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: true,
|
||||||
|
planetColor: 0x40a050 // Dezentere Salbei-Grün-Variante
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'planet_btn_kappa',
|
||||||
|
name: 'Kappa',
|
||||||
|
coords: '1:22:8',
|
||||||
|
isActive: false,
|
||||||
|
hasMoon: false,
|
||||||
|
planetColor: 0xa050a0 // Dezentere Violett-Variante
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// RECHTES MENÜ (PLANETEN-LISTE) RENDER-LOGIK
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut die HTML-Struktur auf. Neues Layout: Strikter 70/30 Split für ALLE Einträge.
|
||||||
|
* Linke Seite (70%): Planeten-Icon, Name, Koordinaten.
|
||||||
|
* Rechte Seite (30%): Mond-Icon (falls vorhanden) oder leerer Platzhalter.
|
||||||
|
* Dadurch bleiben alle Planeten vertikal perfekt in einer Linie.
|
||||||
|
*/
|
||||||
|
export function renderPlanetMenu(pixiPlanetItems) {
|
||||||
|
const container = document.getElementById('right-sidebar-container');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
planetConfig.forEach(planet => {
|
||||||
|
const planetItem = document.createElement('div');
|
||||||
|
planetItem.className = 'planet-item';
|
||||||
|
planetItem.id = planet.id;
|
||||||
|
|
||||||
|
// --- LINKE SEITE (70%) ---
|
||||||
|
const planetArea = document.createElement('div');
|
||||||
|
planetArea.className = 'planet-click-area';
|
||||||
|
planetArea.id = `planet_area_${planet.id}`;
|
||||||
|
planetArea.style.width = '70%';
|
||||||
|
|
||||||
|
const planetAnchor = document.createElement('div');
|
||||||
|
planetAnchor.className = 'planet-icon-anchor';
|
||||||
|
planetAnchor.id = `planet_anchor_${planet.id}`;
|
||||||
|
|
||||||
|
const textWrapper = document.createElement('div');
|
||||||
|
textWrapper.className = 'planet-text-wrapper';
|
||||||
|
|
||||||
|
const nameSpan = document.createElement('span');
|
||||||
|
nameSpan.className = 'planet-name';
|
||||||
|
nameSpan.textContent = planet.name;
|
||||||
|
|
||||||
|
const coordsSpan = document.createElement('span');
|
||||||
|
coordsSpan.className = 'planet-coords';
|
||||||
|
coordsSpan.textContent = `[${planet.coords}]`;
|
||||||
|
|
||||||
|
textWrapper.appendChild(nameSpan);
|
||||||
|
textWrapper.appendChild(coordsSpan);
|
||||||
|
planetArea.appendChild(planetAnchor);
|
||||||
|
planetArea.appendChild(textWrapper);
|
||||||
|
|
||||||
|
planetArea.addEventListener('click', () => {
|
||||||
|
console.log(`[Planeten-Bereich] Klick auf Planet: ${planet.name}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
planetItem.appendChild(planetArea);
|
||||||
|
|
||||||
|
// --- RECHTE SEITE (30%) ---
|
||||||
|
const moonArea = document.createElement('div');
|
||||||
|
moonArea.className = 'moon-click-area';
|
||||||
|
moonArea.id = `moon_area_${planet.id}`;
|
||||||
|
moonArea.style.width = '30%';
|
||||||
|
|
||||||
|
if (planet.hasMoon) {
|
||||||
|
moonArea.classList.add('has-moon');
|
||||||
|
const moonAnchor = document.createElement('div');
|
||||||
|
moonAnchor.className = 'moon-icon-anchor';
|
||||||
|
moonAnchor.id = `moon_anchor_${planet.id}`;
|
||||||
|
moonArea.appendChild(moonAnchor);
|
||||||
|
|
||||||
|
moonArea.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
console.log(`[Mond-Bereich] Klick auf Mond von: ${planet.name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
planetItem.appendChild(moonArea);
|
||||||
|
|
||||||
|
// --- HOVER-LOGIK (PixiJS-Rahmen) ---
|
||||||
|
planetItem.addEventListener('mouseenter', () => {
|
||||||
|
if (pixiPlanetItems && pixiPlanetItems[planet.id]) {
|
||||||
|
pixiPlanetItems[planet.id].alpha = 0.6;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
planetItem.addEventListener('mouseleave', () => {
|
||||||
|
if (pixiPlanetItems && pixiPlanetItems[planet.id]) {
|
||||||
|
// Aktive Planeten behalten eine Grund-Deckkraft
|
||||||
|
pixiPlanetItems[planet.id].alpha = planet.isActive ? 0.45 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(planetItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#resource-header {
|
||||||
|
position: absolute;
|
||||||
|
top: 45px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resource-bar {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo-anchor {
|
||||||
|
width: 250px;
|
||||||
|
height: 60px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 60px; /* Platz wischen Logo und der Ressourcenbar*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.res-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: rgba(0, 0, 0, 0.01);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: help;
|
||||||
|
pointer-events: auto;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.res-box {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-res-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover-Effekt anpassen */
|
||||||
|
.res-col:hover .res-box {
|
||||||
|
border-color: var(--color-cyan-bright);
|
||||||
|
box-shadow: 0 0 10px rgba(0, 240, 255, 0.4);
|
||||||
|
background-color: rgba(0, 240, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.res-val {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-main);
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.res-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--color-cyan-bright);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.res-col.premium .res-box {
|
||||||
|
border-color: rgba(255, 0, 255, 0.4);
|
||||||
|
}
|
||||||
|
.res-col.premium .res-icon {
|
||||||
|
color: #ff00ff;
|
||||||
|
}
|
||||||
|
.res-col.premium:hover .res-box {
|
||||||
|
border-color: #ff00ff;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 255, 0.5);
|
||||||
|
background-color: rgba(255, 0, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.res-divider {
|
||||||
|
/*width: 1px;*/
|
||||||
|
height: 60px;
|
||||||
|
background: var(--color-cyan-dark);
|
||||||
|
opacity: 0.3;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* Formatiert Ressourcen-Werte nach unseren Vorgaben:
|
||||||
|
* - Unter 10.000.000: Normale Darstellung mit Tausendertrennzeichen
|
||||||
|
* - Ab 10.000.000: Abgekürzte Darstellung (z.B. 10,5 Mio)
|
||||||
|
* - Ab 1.000.000.000: Abgekürzte Darstellung (z.B. 1,2 Mrd)
|
||||||
|
* * @param {number} value - Der rohe Zahlenwert aus der Datenbank/Engine
|
||||||
|
* @returns {string} - Der fertig formatierte String für das HTML-HUD
|
||||||
|
*/
|
||||||
|
export function formatResourceNumber(value) {
|
||||||
|
if (value === undefined || value === null || isNaN(value)) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < 10000000) {
|
||||||
|
return value.toLocaleString('de-DE');
|
||||||
|
} else if (value >= 1000000000) {
|
||||||
|
const formatted = (value / 1000000000).toFixed(1);
|
||||||
|
return `${formatted.replace('.', ',')} Mrd`;
|
||||||
|
} else {
|
||||||
|
const formatted = (value / 1000000).toFixed(1);
|
||||||
|
return `${formatted.replace('.', ',')} Mio`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert Zahlen exakt (ohne Abkürzungen wie "Mio"),
|
||||||
|
* aber mit deutschen Tausendertrennzeichen für die Tooltips.
|
||||||
|
*/
|
||||||
|
export function formatExactNumber(value) {
|
||||||
|
if (value === undefined || value === null || isNaN(value)) return '0';
|
||||||
|
return value.toLocaleString('de-DE');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// UPDATE-LOGIK
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht die passenden HTML-Elemente anhand ihrer IDs und schreibt
|
||||||
|
* die formatierten Werte aus einem Daten-Objekt hinein.
|
||||||
|
* * @param {Object} data - Das Objekt mit den aktuellen Sektor-Daten
|
||||||
|
*/
|
||||||
|
export function updateResourceUI(data) {
|
||||||
|
// Aktualisiert Wert, Tooltip und prüft auf Lager-Überlauf
|
||||||
|
const updateMainRes = (idKey, name, val, cap) => {
|
||||||
|
const valEl = document.getElementById(`val_${idKey}`);
|
||||||
|
const wrapperEl = document.getElementById(`wrapper_${idKey}`);
|
||||||
|
|
||||||
|
// Gekürzte Zahl im UI anzeigen (z.B. 14,5 Mio)
|
||||||
|
if (valEl) valEl.textContent = formatResourceNumber(val);
|
||||||
|
|
||||||
|
// Tooltip mit exakten Zahlen generieren
|
||||||
|
// Fallback auf 0, falls noch keine Kapazität vom Backend geliefert wurde
|
||||||
|
const safeCap = cap || 0;
|
||||||
|
const tooltipText = `${name}\nMenge: ${formatExactNumber(val)}\nLagerkapazität: ${formatExactNumber(safeCap)}`;
|
||||||
|
if (wrapperEl) wrapperEl.setAttribute('title', tooltipText);
|
||||||
|
|
||||||
|
// Farbe prüfen: Rot, wenn Menge die Kapazität übersteigt
|
||||||
|
if (valEl) {
|
||||||
|
if (val >= safeCap) {
|
||||||
|
valEl.style.color = 'var(--color-error)';
|
||||||
|
} else {
|
||||||
|
valEl.style.color = 'var(--color-text-main)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Aufruf der Hilfsfunktion für die 4 Hauptressourcen
|
||||||
|
updateMainRes('res_primary', 'Titanium', data.res_primary, data.cap_primary);
|
||||||
|
updateMainRes('res_secondary', 'Silizium', data.res_secondary, data.cap_secondary);
|
||||||
|
updateMainRes('res_fuel_base', 'Deuterium', data.res_fuel_base, data.cap_fuel_base);
|
||||||
|
updateMainRes('res_fuel_adv', 'Tritium', data.res_fuel_adv, data.cap_fuel_adv);
|
||||||
|
|
||||||
|
// --- Energie ---
|
||||||
|
const energyEl = document.getElementById('val_energy');
|
||||||
|
if (energyEl) {
|
||||||
|
energyEl.textContent = formatResourceNumber(data.energy);
|
||||||
|
// Rot färben, wenn wir im Minus sind
|
||||||
|
if (data.energy < 0) {
|
||||||
|
energyEl.style.color = 'var(--color-error)';
|
||||||
|
} else {
|
||||||
|
energyEl.style.color = 'var(--color-text-main)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Bevölkerung & Zufriedenheit (Aggregiert) ---
|
||||||
|
// Gesamtsumme berechnen
|
||||||
|
const popTotal = (data.pop_idle || 0) + (data.pop_work || 0) + (data.pop_injured || 0);
|
||||||
|
document.getElementById('val_pop_total').textContent = formatResourceNumber(popTotal);
|
||||||
|
|
||||||
|
// Tooltip-String aufbauen
|
||||||
|
const popTooltip = `Bevölkerung Details:\nIdle: ${formatResourceNumber(data.pop_idle)}\nArbeitend: ${formatResourceNumber(data.pop_work)}\nVerletzt: ${formatResourceNumber(data.pop_injured)}`;
|
||||||
|
document.getElementById('wrapper_pop').setAttribute('title', popTooltip);
|
||||||
|
|
||||||
|
// Zufriedenheit verarbeiten
|
||||||
|
const happiness = data.pop_happiness !== undefined ? data.pop_happiness : 100;
|
||||||
|
document.getElementById('val_pop_happiness').textContent = `${happiness}%`;
|
||||||
|
|
||||||
|
// Dynamische Emote-Logik basierend auf deinen Vorgaben
|
||||||
|
let happyIcon = '😊'; // Standard-Wert
|
||||||
|
if (happiness <= 20) {
|
||||||
|
happyIcon = '😠'; // Sauer
|
||||||
|
} else if (happiness <= 40) {
|
||||||
|
happyIcon = '😢'; // Weinend
|
||||||
|
} else if (happiness <= 60) {
|
||||||
|
happyIcon = '😐'; // Neutral
|
||||||
|
} else if (happiness <= 80) {
|
||||||
|
happyIcon = '🙂'; // Leicht lächelnd
|
||||||
|
} else if (happiness <= 100) {
|
||||||
|
happyIcon = '😊'; // Glücklich
|
||||||
|
} else {
|
||||||
|
happyIcon = '😍'; // Herzen in den Augen (> 100)
|
||||||
|
}
|
||||||
|
document.getElementById('icon_happiness').textContent = happyIcon;
|
||||||
|
|
||||||
|
// --- Nahrung (Aggregiert) ---
|
||||||
|
// Gesamtsumme berechnen
|
||||||
|
const foodTotal = (data.res_food_base || 0) + (data.res_food_adv || 0);
|
||||||
|
document.getElementById('val_res_food_total').textContent = formatResourceNumber(foodTotal);
|
||||||
|
|
||||||
|
// Tooltip-String aufbauen
|
||||||
|
const foodTooltip = `Nahrung Details:\nGrundnahrung: ${formatResourceNumber(data.res_food_base)}\nVerarbeitet: ${formatResourceNumber(data.res_food_adv)}`;
|
||||||
|
document.getElementById('wrapper_food').setAttribute('title', foodTooltip);
|
||||||
|
|
||||||
|
// --- Premium-Währung (Void Kristalle) ---
|
||||||
|
// Wir zeigen die Gesamtsumme im UI an
|
||||||
|
document.getElementById('val_res_premium').textContent = formatResourceNumber(data.res_premium);
|
||||||
|
|
||||||
|
// Tooltip-String für die Aufschlüsselung (Global vs Lokal) aufbauen
|
||||||
|
const premiumTooltip = `Void Kristalle:\nGlobal: ${formatResourceNumber(data.res_premium_global)}\nLokal: ${formatResourceNumber(data.res_premium_local)}`;
|
||||||
|
document.getElementById('wrapper_premium').setAttribute('title', premiumTooltip);
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#side-menu-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 160px;
|
||||||
|
left: 20px;
|
||||||
|
width: 210px;
|
||||||
|
bottom: 60px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 20;
|
||||||
|
padding-right: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
position: relative;
|
||||||
|
width: 200px;
|
||||||
|
height: 30px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 70px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-text {
|
||||||
|
color: var(--color-text-main);
|
||||||
|
font-size: 14px;
|
||||||
|
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
||||||
|
transition: color 0.2s ease, text-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item:hover .menu-text {
|
||||||
|
color: var(--color-text-main);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar-Styling exklusiv für das Seitenmenü */
|
||||||
|
#side-menu-container::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#side-menu-container::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#side-menu-container::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-cyan-dark);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#side-menu-container::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--color-cyan-bright);
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* Das datengetriebene Array für unsere linke Navigationsleiste.
|
||||||
|
* Wir haben die Liste optimiert: Kernfunktionen nutzen Tabs,
|
||||||
|
* während oft genutzte Features (Galaxie, Netzwerk) als Schnellzugriff bleiben.
|
||||||
|
* * Eigenschaften:
|
||||||
|
* - id: Eindeutige HTML-ID für die PixiJS-Synchronisation
|
||||||
|
* - label: Der angezeigte Text im UI
|
||||||
|
* - unlocked: Steuert die Sichtbarkeit im Menü
|
||||||
|
* - actionId: Der Identifier für den Klick-Event-Listener
|
||||||
|
*/
|
||||||
|
export const menuConfig = [
|
||||||
|
{
|
||||||
|
id: 'menu_btn_dashboard',
|
||||||
|
label: 'Dashboard',
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_dashboard'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_communication',
|
||||||
|
label: 'Kommunikation',
|
||||||
|
// Beinhaltet später Tabs: Nachrichten, Chat
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_communication'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_infrastructure',
|
||||||
|
label: 'Infrastruktur',
|
||||||
|
// Beinhaltet später Tabs: Verwaltung, Produktion, Zivile Gebäude, Anlagen
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_infrastructure'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_military',
|
||||||
|
label: 'Militär',
|
||||||
|
// Beinhaltet NUR Bau-Aspekte: Schiffswerft (Flotte bauen), Verteidigung (Anlagen bauen)
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_military'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_research',
|
||||||
|
label: 'Forschung',
|
||||||
|
// Beinhaltet später Tabs: Lokale Forschung, Globale Forschung
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_research'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_dispatch',
|
||||||
|
label: 'Flotte versenden',
|
||||||
|
// Beinhaltet den aktiven Flottenversand: Transporte, Angriffe, Stationierungen
|
||||||
|
// und Einsätze wie die "Lokale Erkundung"
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_dispatch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_galaxy',
|
||||||
|
label: 'Galaxie',
|
||||||
|
// Direkter Absprung auf die Sternenkarte
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_galaxy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_alliance',
|
||||||
|
label: 'Allianz',
|
||||||
|
// Direkter Zugriff auf die Allianz-Verwaltung
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_alliance'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_friends',
|
||||||
|
label: 'Freunde',
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_friends'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_highscore',
|
||||||
|
label: 'Highscore',
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_highscore'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_search',
|
||||||
|
label: 'Suche',
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_search'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_void_gate',
|
||||||
|
label: 'Void Gate',
|
||||||
|
// Zugang zu PvE und Händler-Tauschbörse.
|
||||||
|
// Kann z.B. false sein, bis eine bestimmte Forschung abgeschlossen ist.
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_void_gate'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'menu_btn_shop',
|
||||||
|
label: 'Shop',
|
||||||
|
unlocked: true,
|
||||||
|
actionId: 'view_shop'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liest das menuConfig-Array aus und baut die HTML-Struktur für die linke Navigation.
|
||||||
|
* Nur Einträge, die 'unlocked: true' haben, werden gerendert.
|
||||||
|
* * Jeder Menüpunkt bekommt:
|
||||||
|
* 1. Die exakte ID aus der Config (damit PixiJS später weiß, wohin die Grafik muss).
|
||||||
|
* 2. Ein data-Attribut für die auszuführende Aktion beim Klick.
|
||||||
|
* 3. Ein Span-Element für den formatierten Text.
|
||||||
|
*/
|
||||||
|
export function renderSideMenu(pixiMenuItems) {
|
||||||
|
const container = document.getElementById('side-menu-container');
|
||||||
|
|
||||||
|
if (!container) return;
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
menuConfig.forEach(item => {
|
||||||
|
if (item.unlocked) {
|
||||||
|
const menuItem = document.createElement('div');
|
||||||
|
menuItem.className = 'menu-item';
|
||||||
|
menuItem.id = item.id;
|
||||||
|
menuItem.dataset.action = item.actionId;
|
||||||
|
|
||||||
|
const textSpan = document.createElement('span');
|
||||||
|
textSpan.className = 'menu-text';
|
||||||
|
textSpan.textContent = item.label;
|
||||||
|
|
||||||
|
menuItem.appendChild(textSpan);
|
||||||
|
|
||||||
|
// Klick-Logik
|
||||||
|
menuItem.addEventListener('click', () => {
|
||||||
|
console.log(`[Menü] Klick auf: ${item.label} (Action: ${item.actionId})`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hover-In Logik (Hintergrund leuchtet auf)
|
||||||
|
menuItem.addEventListener('mouseenter', () => {
|
||||||
|
// Sicherheitsprüfung: Wurden die Pixi-Sprites bereits generiert?
|
||||||
|
if (pixiMenuItems && pixiMenuItems[item.id] && pixiMenuItems[item.id].bgSprite) {
|
||||||
|
pixiMenuItems[item.id].bgSprite.tint = 0x00f0ff;
|
||||||
|
pixiMenuItems[item.id].bgSprite.alpha = 0.9;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hover-Out Logik (Hintergrund zurück auf Standard)
|
||||||
|
menuItem.addEventListener('mouseleave', () => {
|
||||||
|
if (pixiMenuItems && pixiMenuItems[item.id] && pixiMenuItems[item.id].bgSprite) {
|
||||||
|
pixiMenuItems[item.id].bgSprite.tint = 0x00a0aa;
|
||||||
|
pixiMenuItems[item.id].bgSprite.alpha = 0.6;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(menuItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
TOP-BAR (Ganz oben, Account- & Sektor-Infos)
|
||||||
|
========================================================================== */
|
||||||
|
#sector-top-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sector-top-bar a {
|
||||||
|
color: var(--color-text-inactive);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease, text-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sector-top-bar a:hover:not(.inactive-link) {
|
||||||
|
color: var(--color-cyan-bright);
|
||||||
|
text-shadow: 0 0 5px var(--color-cyan-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links, die derzeit noch keine Funktion haben, visuell abstufen */
|
||||||
|
.inactive-link {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sector-name {
|
||||||
|
color: var(--color-text-main);
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Initialisiert die Top-Bar-Komponente.
|
||||||
|
* Hier werden wir später Logik hinzufügen, um z.B. den
|
||||||
|
* Account-Namen oder den Sektor-Namen aus den echten Backend-Daten
|
||||||
|
* zu aktualisieren.
|
||||||
|
*/
|
||||||
|
export function initTopbar() {
|
||||||
|
const topbarElement = document.getElementById('sector-top-bar');
|
||||||
|
|
||||||
|
if (!topbarElement) {
|
||||||
|
console.warn('[Topbar] Konnte #sector-top-bar nicht im DOM finden.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backBtn = document.getElementById('btn-back-to-lobby');
|
||||||
|
if (backBtn) {
|
||||||
|
// Hier könnten wir später vorm Verlassen des Sektors
|
||||||
|
// noch Daten speichern oder abmelden (Logout-Logik).
|
||||||
|
//console.log('[Topbar] Lobby-Button ist einsatzbereit.');
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('[Topbar] Erfolgreich initialisiert.');
|
||||||
|
}
|
||||||
@@ -1,393 +0,0 @@
|
|||||||
/* ==========================================================================
|
|
||||||
TOP-BAR (Ganz oben, Account- & Sektor-Infos)
|
|
||||||
========================================================================== */
|
|
||||||
#sector-top-bar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 30px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--color-text-inactive);
|
|
||||||
}
|
|
||||||
|
|
||||||
#sector-top-bar a {
|
|
||||||
color: var(--color-text-inactive);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.2s ease, text-shadow 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sector-top-bar a:hover:not(.inactive-link) {
|
|
||||||
color: var(--color-cyan-bright);
|
|
||||||
text-shadow: 0 0 5px var(--color-cyan-bright);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Links, die derzeit noch keine Funktion haben, visuell abstufen */
|
|
||||||
.inactive-link {
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sector-name {
|
|
||||||
color: var(--color-text-main);
|
|
||||||
font-weight: bold;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
RESSOURCEN-HEADER (Unter der Top-Bar)
|
|
||||||
========================================================================== */
|
|
||||||
#resource-header {
|
|
||||||
position: absolute;
|
|
||||||
top: 45px;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 80px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
pointer-events: auto;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- RESSOURCEN MITTIG --- */
|
|
||||||
#resource-bar {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.res-col {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 8px;
|
|
||||||
background: rgba(0, 0, 0, 0.01);
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: help;
|
|
||||||
pointer-events: auto;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Der Rahmen für das Icon */
|
|
||||||
.res-box {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--color-res-border);
|
|
||||||
border-radius: 6px;
|
|
||||||
width: 45px;
|
|
||||||
height: 45px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
pointer-events: none;
|
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hover-Effekt anpassen */
|
|
||||||
.res-col:hover .res-box {
|
|
||||||
border-color: var(--color-cyan-bright);
|
|
||||||
box-shadow: 0 0 10px rgba(0, 240, 255, 0.4);
|
|
||||||
background-color: rgba(0, 240, 255, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.res-val {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--color-text-main);
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.res-icon {
|
|
||||||
font-size: 24px;
|
|
||||||
color: var(--color-cyan-bright);
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.res-col.premium .res-box {
|
|
||||||
border-color: rgba(255, 0, 255, 0.4);
|
|
||||||
}
|
|
||||||
.res-col.premium .res-icon {
|
|
||||||
color: #ff00ff;
|
|
||||||
}
|
|
||||||
.res-col.premium:hover .res-box {
|
|
||||||
border-color: #ff00ff;
|
|
||||||
box-shadow: 0 0 10px rgba(255, 0, 255, 0.5);
|
|
||||||
background-color: rgba(255, 0, 255, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.res-divider {
|
|
||||||
/*width: 1px;*/
|
|
||||||
height: 60px;
|
|
||||||
background: var(--color-cyan-dark);
|
|
||||||
opacity: 0.3;
|
|
||||||
margin: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
SIDE-MENU (Linke Navigation)
|
|
||||||
========================================================================== */
|
|
||||||
#side-menu-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 160px;
|
|
||||||
left: 20px;
|
|
||||||
width: 210px;
|
|
||||||
bottom: 60px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
overflow-x: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15px;
|
|
||||||
pointer-events: auto;
|
|
||||||
z-index: 20;
|
|
||||||
padding-right: 15px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
scrollbar-gutter: stable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Der einzelne, klickbare Wrapper für einen Menüpunkt */
|
|
||||||
.menu-item {
|
|
||||||
position: relative;
|
|
||||||
width: 200px;
|
|
||||||
height: 30px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 70px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Der Text innerhalb des Menüpunkts */
|
|
||||||
.menu-text {
|
|
||||||
color: var(--color-text-main);
|
|
||||||
font-size: 14px;
|
|
||||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
|
||||||
transition: color 0.2s ease, text-shadow 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Schöner Hover-Effekt: Text leuchtet in unserem Cyan auf */
|
|
||||||
.menu-item:hover .menu-text {
|
|
||||||
color: var(--color-text-main);
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
PLANETEN-LIMIT (Zähler über der rechten Sidebar)
|
|
||||||
========================================================================== */
|
|
||||||
#planet-limit-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 100px;
|
|
||||||
right: 20px;
|
|
||||||
width: 215px;
|
|
||||||
height: 45px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 20;
|
|
||||||
/* Da es ein reines Info-Element ist, deaktivieren wir die Klicks */
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.planet-limit-text {
|
|
||||||
color: var(--color-text-main);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
|
||||||
/* Ein kleiner Abstand zwischen Buchstaben (Letter-Spacing) lässt
|
|
||||||
Sci-Fi UIs oft etwas hochwertiger wirken */
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#val_planet_count {
|
|
||||||
color: var(--color-cyan-bright); /* Aktuelle Planetenanzahl hervorheben */
|
|
||||||
}
|
|
||||||
|
|
||||||
#val_planet_max {
|
|
||||||
color: var(--color-text-inactive); /* Das Limit etwas abdunkeln */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
RIGHT-SIDEBAR (Rechte Planeten-Navigation)
|
|
||||||
========================================================================== */
|
|
||||||
#right-sidebar-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 160px;
|
|
||||||
right: 20px;
|
|
||||||
width: 215px;
|
|
||||||
|
|
||||||
bottom: 60px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
overflow-x: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 25px;
|
|
||||||
pointer-events: auto;
|
|
||||||
z-index: 20;
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 30px;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
scrollbar-gutter: stable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
PLANETEN-EINTRÄGE (70/30 HTML-Struktur)
|
|
||||||
========================================================================== */
|
|
||||||
.planet-item {
|
|
||||||
position: relative;
|
|
||||||
width: 200px;
|
|
||||||
height: 90px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
/* Wir nutzen jetzt eine Reihe (Row), um die Bereiche nebeneinander zu legen */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: stretch; /* Beide Bereiche füllen die volle Höhe von 90px aus */
|
|
||||||
justify-content: flex-start;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Der 70% Hauptbereich für den Planeten */
|
|
||||||
.planet-click-area {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 6px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Der 30% Nebenbereich für den Mond */
|
|
||||||
.moon-click-area {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
/* Standardmäßig kein Pointer, da die Box leer sein könnte */
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Trennlinie und Klick-Cursor NUR anzeigen, wenn auch wirklich ein Mond da ist */
|
|
||||||
.moon-click-area.has-moon {
|
|
||||||
cursor: pointer;
|
|
||||||
border-left: 1px solid rgba(0, 240, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HTML-Anker für PixiJS */
|
|
||||||
.planet-icon-anchor {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.moon-icon-anchor {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
z-index: 25;
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hover-Effekte skalieren das Icon, wenn der Spieler im 30%-Feld ist */
|
|
||||||
.moon-click-area:hover .moon-icon-anchor {
|
|
||||||
transform: scale(1.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.planet-text-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.planet-name {
|
|
||||||
color: var(--color-text-main);
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
|
||||||
transition: color 0.2s ease, text-shadow 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.planet-coords {
|
|
||||||
color: var(--color-text-inactive);
|
|
||||||
font-size: 11px;
|
|
||||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
SCROLLBAR-STYLING FÜR WEBKIT (Chrome, Safari, Edge)
|
|
||||||
========================================================================== */
|
|
||||||
/* Definiert die Breite der Scrollbar für beide Menüs */
|
|
||||||
#side-menu-container::-webkit-scrollbar,
|
|
||||||
#right-sidebar-container::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Die sichtbare "Spur" (Track), auf der der Scrollbalken läuft */
|
|
||||||
#side-menu-container::-webkit-scrollbar-track,
|
|
||||||
#right-sidebar-container::-webkit-scrollbar-track {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Der sichtbare, greifbare Teil des Scrollbalkens (Thumb) */
|
|
||||||
#side-menu-container::-webkit-scrollbar-thumb,
|
|
||||||
#right-sidebar-container::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--color-cyan-dark);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hover-Effekt: Der Anfasser leuchtet im helleren Cyan auf */
|
|
||||||
#side-menu-container::-webkit-scrollbar-thumb:hover,
|
|
||||||
#right-sidebar-container::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: var(--color-cyan-bright);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
FOOTER
|
|
||||||
========================================================================== */
|
|
||||||
#game-footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 40px;
|
|
||||||
background: var(--color-panel-bg);
|
|
||||||
border-top: 1px solid var(--color-cyan-dark);
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 40px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: var(--color-text-inactive);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-left { flex: 1; text-align: left; }
|
|
||||||
.footer-center { flex: 1; text-align: center; }
|
|
||||||
.footer-right { flex: 1; text-align: right; }
|
|
||||||
|
|
||||||
#game-footer a {
|
|
||||||
color: var(--color-text-inactive);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.2s ease, text-shadow 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-footer a:hover {
|
|
||||||
color: var(--color-cyan-bright);
|
|
||||||
text-shadow: 0 0 5px var(--color-cyan-bright);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -9,24 +9,27 @@ body, html {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
background-color: var(--color-bg-page);
|
background-color: var(--color-bg-page);
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
text-size-adjust: 100%;
|
text-size-adjust: 100%;
|
||||||
position: fixed;
|
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#game-wrapper {
|
#game-wrapper {
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
min-width: 1200px;
|
||||||
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Der Container für den PixiJS-Hintergrund und die Spielwelt */
|
/* Der Container für den PixiJS-Hintergrund und die Spielwelt */
|
||||||
#pixi-container {
|
#pixi-container {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -38,13 +41,25 @@ body, html {
|
|||||||
#ui-layer {
|
#ui-layer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
right: 0;
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ui-safe-zone {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
min-width: 1200px;
|
||||||
|
max-width: 1600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* * Alle interaktiven HUD-Elemente müssen pointer-events wieder aktivieren,
|
/* * Alle interaktiven HUD-Elemente müssen pointer-events wieder aktivieren,
|
||||||
* sonst können wir keine Links klicken oder Hover-Effekte auslösen.
|
* sonst können wir keine Links klicken oder Hover-Effekte auslösen.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { updateResourceUI } from '../components/resource-header/resource-header.js';
|
||||||
|
|
||||||
|
// Wir lagern die Daten in ein reaktives Zustandsobjekt aus.
|
||||||
|
// Später wird dieses Objekt durch die echte Backend-API befüllt
|
||||||
|
// (z.B. über WebSockets oder Polling, ähnlich wie in der Lobby).
|
||||||
|
const sectorState = {
|
||||||
|
res_primary: 1000, cap_primary: 10000,
|
||||||
|
res_secondary: 500, cap_secondary: 10000,
|
||||||
|
res_fuel_base: 0,cap_fuel_base: 10000,
|
||||||
|
res_fuel_adv: 0, cap_fuel_adv: 10000,
|
||||||
|
|
||||||
|
energy: 0,
|
||||||
|
|
||||||
|
pop_idle: 500,
|
||||||
|
pop_work: 0,
|
||||||
|
pop_injured: 500,
|
||||||
|
pop_happiness: 50,
|
||||||
|
res_food_base: 10000,
|
||||||
|
res_food_adv: 0,
|
||||||
|
res_premium: 150,
|
||||||
|
res_premium_global: 100,
|
||||||
|
res_premium_local: 50
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hilfsfunktion für gedeckelte Ressourcen-Produktion.
|
||||||
|
* Erhöht den aktuellen Wert um 'amount', stoppt aber exakt am 'cap'.
|
||||||
|
* Ist der Wert bereits über dem Cap (z.B. durch Startwerte oder Boni),
|
||||||
|
* wird nichts abgezogen, aber die Produktion pausiert.
|
||||||
|
*
|
||||||
|
* @param {number} currentValue - Der derzeitige Bestand
|
||||||
|
* @param {number} productionAmount - Menge, die hinzugefügt wird
|
||||||
|
* @param {number} capacity - Das maximale Lagerlimit
|
||||||
|
*
|
||||||
|
* @returns {number} - Der neue berechnete Bestand
|
||||||
|
*/
|
||||||
|
function addResourceWithCap(currentValue, productionAmount, capacity) {
|
||||||
|
if (currentValue >= capacity) {
|
||||||
|
return currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newValue = currentValue + productionAmount;
|
||||||
|
if (newValue > capacity) {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Startet die Simulation und den Interval-Timer für die Ressourcen-Produktion.
|
||||||
|
* Initiiert zudem das erste sofortige UI-Update.
|
||||||
|
*/
|
||||||
|
export function startSimulation() {
|
||||||
|
// 1. Initiales Update beim Start, um die Nullen im HUD zu überschreiben
|
||||||
|
updateResourceUI(sectorState);
|
||||||
|
|
||||||
|
// 2. Der simulierte Game-Loop (1 Tick pro Sekunde)
|
||||||
|
setInterval(() => {
|
||||||
|
// Ressourcen berechnen
|
||||||
|
sectorState.res_primary = addResourceWithCap(sectorState.res_primary, 15, sectorState.cap_primary);
|
||||||
|
sectorState.res_secondary = addResourceWithCap(sectorState.res_secondary, 5, sectorState.cap_secondary);
|
||||||
|
|
||||||
|
// Nahrung wird hier beispielhaft verbraucht
|
||||||
|
sectorState.res_food_base -= 10;
|
||||||
|
|
||||||
|
// 3. UI mit den neuen Werten aktualisieren
|
||||||
|
updateResourceUI(sectorState);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
console.log('[Simulation] Game-Loop gestartet.');
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,356 @@
|
|||||||
|
import { Sprite, Container, Text, Graphics, NineSliceSprite } from 'pixi.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt das Sprite für den Sektor-Hintergrund und fügt es der Stage hinzu.
|
||||||
|
*
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz.
|
||||||
|
* @param {Texture} bgTexture - Die zuvor geladene Hintergrund-Textur.
|
||||||
|
*
|
||||||
|
* @returns {Sprite} - Das erstellte Hintergrund-Sprite (wird für Resize benötigt).
|
||||||
|
*/
|
||||||
|
export function buildBackground(app, bgTexture) {
|
||||||
|
const background = new Sprite(bgTexture);
|
||||||
|
app.stage.addChild(background);
|
||||||
|
|
||||||
|
console.log('[PixiBuilder] Hintergrund generiert.');
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut den Logo-Container (Ring-Grafik + Text) zusammen und fügt ihn der Stage hinzu.
|
||||||
|
*
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz.
|
||||||
|
* @param {Object} frameSheet - Das geladene Sprite-Sheet für die UI-Rahmen.
|
||||||
|
*
|
||||||
|
* @returns {Container} - Der fertige Logo-Container (wird für DOM-Sync benötigt).
|
||||||
|
*/
|
||||||
|
export function buildLogo(app, frameSheet) {
|
||||||
|
const logoContainer = new Container();
|
||||||
|
|
||||||
|
// 1. Den leuchtenden Ring im Hintergrund des Logos erstellen
|
||||||
|
const ringTexture = frameSheet.textures['SPR_SciFiMenus_Ring_Medium_13'];
|
||||||
|
if (ringTexture) {
|
||||||
|
const logoRing = new Sprite(ringTexture);
|
||||||
|
logoRing.anchor.set(0.5); // Anker in die Mitte für saubere Zentrierung
|
||||||
|
logoRing.tint = 0x00f0ff; // Unser charakteristisches Cyan
|
||||||
|
logoRing.scale.set(0.12);
|
||||||
|
logoRing.alpha = 0.8;
|
||||||
|
logoContainer.addChild(logoRing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Den Schriftzug erstellen
|
||||||
|
const logoText = new Text({
|
||||||
|
text: 'Void-Genesis',
|
||||||
|
style: {
|
||||||
|
fontFamily: 'Logo Font',
|
||||||
|
fontSize: 33,
|
||||||
|
fill: 0x00f0ff,
|
||||||
|
letterSpacing: 6,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logoText.anchor.set(0.5);
|
||||||
|
logoContainer.addChild(logoText);
|
||||||
|
|
||||||
|
// 3. Den gesamten Container auf die Bühne legen
|
||||||
|
app.stage.addChild(logoContainer);
|
||||||
|
|
||||||
|
console.log('[PixiBuilder] Logo generiert.');
|
||||||
|
return logoContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generiert die PixiJS-Icons für die Ressourcenleiste (Titanium, Energie, Pop, etc.).
|
||||||
|
* Erstellt die Hintergründe, lädt die Sprites aus dem Sheet und wendet Masken an.
|
||||||
|
*
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz.
|
||||||
|
* @param {Object} iconSheet - Das geladene Sprite-Sheet für die Icons.
|
||||||
|
*
|
||||||
|
* @returns {Object} - Ein Dictionary mit den fertigen Containern { 'html_id': Container } für den DOM-Sync Ticker.
|
||||||
|
*/
|
||||||
|
export function buildResourceIcons(app, iconSheet) {
|
||||||
|
const pixiIcons = {};
|
||||||
|
|
||||||
|
// Zuordnung: HTML-ID der Box -> Textur-Name im Sprite-Sheet
|
||||||
|
const iconMapping = {
|
||||||
|
'box_primary': 'ICON_Titanium',
|
||||||
|
'box_secondary': 'ICON_Silizium',
|
||||||
|
'box_fuel_base': 'ICON_Deuterium',
|
||||||
|
'box_fuel_adv': 'ICON_Tritium',
|
||||||
|
'box_energy': 'ICON_Map_Lightning',
|
||||||
|
'box_pop_total': 'ICON_SciFiMenus_Menu_Multiplayer_01_Clean',
|
||||||
|
'box_food_total': 'ICON_SciFi_Status_Hunger',
|
||||||
|
'box_premium': 'ICON_SM_Item_Crystal_03'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (iconSheet) {
|
||||||
|
for (const boxId in iconMapping) {
|
||||||
|
const texName = iconMapping[boxId];
|
||||||
|
|
||||||
|
if (iconSheet.textures[texName]) {
|
||||||
|
const boxContainer = new Container();
|
||||||
|
|
||||||
|
// Dunkler Hintergrund für das Icon
|
||||||
|
const bgGraphic = new Graphics();
|
||||||
|
bgGraphic.roundRect(-22.5, -22.5, 45, 45, 6);
|
||||||
|
bgGraphic.fill({ color: 0x000000, alpha: 0.6 });
|
||||||
|
boxContainer.addChild(bgGraphic);
|
||||||
|
|
||||||
|
// Das eigentliche Icon-Sprite
|
||||||
|
const sprite = new Sprite(iconSheet.textures[texName]);
|
||||||
|
sprite.anchor.set(0.5);
|
||||||
|
|
||||||
|
// Spezifische Skalierungen und Masken je nach Ressourcen-Typ
|
||||||
|
if (boxId == 'box_primary' || boxId == 'box_secondary' || boxId == 'box_fuel_base' || boxId == 'box_fuel_adv') {
|
||||||
|
sprite.scale.set(0.50);
|
||||||
|
// Die 4 Hauptressourcen bekommen eine Schnittmaske
|
||||||
|
const mask = new Graphics();
|
||||||
|
mask.roundRect(-22.5, -22.5, 45, 45, 6);
|
||||||
|
mask.fill(0xffffff);
|
||||||
|
sprite.mask = mask;
|
||||||
|
boxContainer.addChild(mask);
|
||||||
|
}
|
||||||
|
else if (boxId == 'box_pop_total' || boxId == 'box_food_total') {
|
||||||
|
sprite.scale.set(0.15);
|
||||||
|
}
|
||||||
|
else if (boxId === 'box_energy') {
|
||||||
|
sprite.scale.set(0.15);
|
||||||
|
sprite.tint = 0xffff00; // Gelb färben
|
||||||
|
}
|
||||||
|
else if (boxId === 'box_premium') {
|
||||||
|
sprite.scale.set(0.15);
|
||||||
|
sprite.tint = 0xff00ff; // Magenta färben
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprite.scale.set(0.12); // Fallback Skalierung
|
||||||
|
}
|
||||||
|
|
||||||
|
boxContainer.addChild(sprite);
|
||||||
|
app.stage.addChild(boxContainer);
|
||||||
|
|
||||||
|
// Den fertigen Container in unser Dictionary speichern
|
||||||
|
pixiIcons[boxId] = boxContainer;
|
||||||
|
} else {
|
||||||
|
console.warn(`[PixiBuilder] Icon-Textur nicht gefunden: ${texName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[PixiBuilder] Ressourcen-Icons generiert.');
|
||||||
|
return pixiIcons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generiert die Hintergrund-Grafiken und leuchtenden Rahmen für das linke Seitenmenü.
|
||||||
|
*
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz.
|
||||||
|
* @param {Object} frameSheet - Das geladene Sprite-Sheet für die UI-Rahmen.
|
||||||
|
* @param {Array} menuConfig - Die Konfiguration der Menüpunkte (aus side-menu.js).
|
||||||
|
*
|
||||||
|
* @returns {Object} - Ein Objekt mit der Maske und dem Dictionary der Menü-Container { pixiMenuMask, pixiMenuItems } für den DOM-Sync Ticker.
|
||||||
|
*/
|
||||||
|
export function buildSideMenuGraphics(app, frameSheet, menuConfig) {
|
||||||
|
const pixiMenuItems = {};
|
||||||
|
|
||||||
|
// Haupt-Container für das Menü
|
||||||
|
const pixiMenuContainer = new Container();
|
||||||
|
app.stage.addChild(pixiMenuContainer);
|
||||||
|
|
||||||
|
// Maske erstellen, damit die Sprites beim Scrollen abgeschnitten werden
|
||||||
|
const pixiMenuMask = new Graphics();
|
||||||
|
app.stage.addChild(pixiMenuMask);
|
||||||
|
pixiMenuContainer.mask = pixiMenuMask;
|
||||||
|
|
||||||
|
// Für jeden freigeschalteten Menüpunkt einen Rahmen generieren
|
||||||
|
menuConfig.forEach(item => {
|
||||||
|
if (item.unlocked && frameSheet) {
|
||||||
|
const bgTexture = frameSheet.textures['SPR_SciFiMenus_Menu_Item_Background_07'];
|
||||||
|
const frameTexture = frameSheet.textures['SPR_SciFiMenus_Menu_Item_Selected_10'];
|
||||||
|
|
||||||
|
if (bgTexture && frameTexture) {
|
||||||
|
const menuContainer = new Container();
|
||||||
|
|
||||||
|
// Der statische Hintergrund (abgedunkeltes Cyan)
|
||||||
|
const bgSprite = new NineSliceSprite({
|
||||||
|
texture: bgTexture,
|
||||||
|
leftWidth: 120,
|
||||||
|
topHeight: 120,
|
||||||
|
rightWidth: 120,
|
||||||
|
bottomHeight: 120
|
||||||
|
});
|
||||||
|
bgSprite.width = 150;
|
||||||
|
bgSprite.height = 30;
|
||||||
|
bgSprite.x = -50;
|
||||||
|
bgSprite.y = -15;
|
||||||
|
bgSprite.tint = 0x00a0aa;
|
||||||
|
bgSprite.alpha = 0.6;
|
||||||
|
|
||||||
|
// Der leuchtende Rahmen (helles Cyan, wird drübergelegt)
|
||||||
|
const frameSprite = new NineSliceSprite({
|
||||||
|
texture: frameTexture,
|
||||||
|
leftWidth: 100,
|
||||||
|
topHeight: 120,
|
||||||
|
rightWidth: 140,
|
||||||
|
bottomHeight: 120
|
||||||
|
});
|
||||||
|
frameSprite.width = 150;
|
||||||
|
frameSprite.height = 30;
|
||||||
|
frameSprite.x = -50;
|
||||||
|
frameSprite.y = -15;
|
||||||
|
frameSprite.tint = 0x00f0ff;
|
||||||
|
frameSprite.alpha = 1;
|
||||||
|
|
||||||
|
// Referenz speichern, damit wir beim Hovern (im DOM) die Farbe ändern können
|
||||||
|
menuContainer.bgSprite = bgSprite;
|
||||||
|
|
||||||
|
menuContainer.addChild(bgSprite);
|
||||||
|
menuContainer.addChild(frameSprite);
|
||||||
|
|
||||||
|
pixiMenuContainer.addChild(menuContainer);
|
||||||
|
pixiMenuItems[item.id] = menuContainer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[PixiBuilder] Linkes Seitenmenü (Grafiken) generiert.');
|
||||||
|
|
||||||
|
// Wir geben beide Objekte zurück, da der Ticker sie benötigt
|
||||||
|
return { pixiMenuMask, pixiMenuItems };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generiert die PixiJS-Grafiken für die rechte Seite: Das Planeten-Limit-Feld
|
||||||
|
* sowie die scrollbare Liste der Planeten (Hintergründe, Planeten-Icons, Monde).
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz.
|
||||||
|
* @param {Object} frameSheet - Das geladene Sprite-Sheet für Rahmen.
|
||||||
|
* @param {Object} iconSheet - Das geladene Sprite-Sheet für die Icons (Planeten/Monde).
|
||||||
|
* @param {Array} planetConfig - Die Konfiguration der Planeten (aus planet-menu.js).
|
||||||
|
*
|
||||||
|
* @returns {Object} - Ein Objekt mit allen benötigten Referenzen für den DOM-Sync Ticker.
|
||||||
|
*/
|
||||||
|
export function buildPlanetMenuGraphics(app, frameSheet, iconSheet, planetConfig) {
|
||||||
|
let pixiPlanetLimitContainer = null;
|
||||||
|
const pixiPlanetItems = {};
|
||||||
|
const pixiPlanetIcons = {};
|
||||||
|
const pixiMoonIcons = {};
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// 1. PLANETEN-LIMIT GRAFIK
|
||||||
|
// ====================================================================
|
||||||
|
if (frameSheet) {
|
||||||
|
const limitBgTexture = frameSheet.textures['SPR_SciFiMenus_Button_Frame_01_Background'];
|
||||||
|
const limitFrameTexture = frameSheet.textures['SPR_SciFiMenus_Button_Frame_01'];
|
||||||
|
|
||||||
|
if (limitBgTexture && limitFrameTexture) {
|
||||||
|
pixiPlanetLimitContainer = new Container();
|
||||||
|
|
||||||
|
// Hintergrund
|
||||||
|
const limitBgSprite = new NineSliceSprite({
|
||||||
|
texture: limitBgTexture,
|
||||||
|
leftWidth: 500, topHeight: 100, rightWidth: 500, bottomHeight: 100
|
||||||
|
});
|
||||||
|
limitBgSprite.width = 165;
|
||||||
|
limitBgSprite.height = 45;
|
||||||
|
limitBgSprite.x = -85.5; // Zentrierung
|
||||||
|
limitBgSprite.y = -22.5;
|
||||||
|
limitBgSprite.tint = 0x00a0aa;
|
||||||
|
limitBgSprite.alpha = 0.5;
|
||||||
|
|
||||||
|
// Leuchtender Rahmen
|
||||||
|
const limitFrameSprite = new NineSliceSprite({
|
||||||
|
texture: limitFrameTexture,
|
||||||
|
leftWidth: 500, topHeight: 100, rightWidth: 500, bottomHeight: 100
|
||||||
|
});
|
||||||
|
limitFrameSprite.width = 200;
|
||||||
|
limitFrameSprite.height = 45;
|
||||||
|
limitFrameSprite.x = -100;
|
||||||
|
limitFrameSprite.y = -22.5;
|
||||||
|
limitFrameSprite.alpha = 1.0;
|
||||||
|
|
||||||
|
pixiPlanetLimitContainer.addChild(limitBgSprite);
|
||||||
|
pixiPlanetLimitContainer.addChild(limitFrameSprite);
|
||||||
|
app.stage.addChild(pixiPlanetLimitContainer); // Direkt auf die Stage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// 2. RECHTE PLANETEN-LISTE GRAFIKEN
|
||||||
|
// ====================================================================
|
||||||
|
|
||||||
|
// Haupt-Container und Maske für das Scrolling erstellen
|
||||||
|
const pixiPlanetContainer = new Container();
|
||||||
|
app.stage.addChild(pixiPlanetContainer);
|
||||||
|
|
||||||
|
const pixiPlanetMask = new Graphics();
|
||||||
|
app.stage.addChild(pixiPlanetMask);
|
||||||
|
pixiPlanetContainer.mask = pixiPlanetMask;
|
||||||
|
|
||||||
|
planetConfig.forEach(planet => {
|
||||||
|
if (frameSheet && iconSheet) {
|
||||||
|
|
||||||
|
// --- A. Hintergrund-Rahmen (NineSlice) ---
|
||||||
|
const bgTexture = frameSheet.textures['SPR_SciFiMenus_Frame_Box_Small_10_Background'];
|
||||||
|
if (bgTexture) {
|
||||||
|
const bgContainer = new Container();
|
||||||
|
const bgSprite = new NineSliceSprite({
|
||||||
|
texture: bgTexture,
|
||||||
|
leftWidth: 30, topHeight: 30, rightWidth: 30, bottomHeight: 30
|
||||||
|
});
|
||||||
|
|
||||||
|
bgSprite.width = 195;
|
||||||
|
bgSprite.height = 105;
|
||||||
|
bgSprite.x = -97.5;
|
||||||
|
bgSprite.y = -52.5;
|
||||||
|
bgSprite.tint = 0x00f0ff;
|
||||||
|
|
||||||
|
bgContainer.addChild(bgSprite);
|
||||||
|
pixiPlanetContainer.addChild(bgContainer);
|
||||||
|
|
||||||
|
// Aktive Planeten leuchten leicht auf, inaktive sind unsichtbar (bis Hover)
|
||||||
|
bgContainer.alpha = planet.isActive ? 0.45 : 0;
|
||||||
|
pixiPlanetItems[planet.id] = bgContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- B. Haupt-Planet Icon ---
|
||||||
|
const planetTexture = iconSheet.textures['ICON_SciFiMenus_Menu_Planet_03_Clean'];
|
||||||
|
if (planetTexture) {
|
||||||
|
const planetContainer = new Container();
|
||||||
|
const planetSprite = new Sprite(planetTexture);
|
||||||
|
|
||||||
|
planetSprite.anchor.set(0.5);
|
||||||
|
planetSprite.scale.set(0.24);
|
||||||
|
planetSprite.tint = planet.planetColor; // Individuelle Planetenfarbe aus Config
|
||||||
|
planetSprite.alpha = 0.8;
|
||||||
|
|
||||||
|
planetContainer.addChild(planetSprite);
|
||||||
|
pixiPlanetContainer.addChild(planetContainer);
|
||||||
|
pixiPlanetIcons[planet.id] = planetContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- C. Mond Icon ---
|
||||||
|
const moonTexture = iconSheet.textures['ICON_SciFiMenus_Menu_Moon_01_Clean'];
|
||||||
|
if (planet.hasMoon && moonTexture) {
|
||||||
|
const moonContainer = new Container();
|
||||||
|
const moonSprite = new Sprite(moonTexture);
|
||||||
|
|
||||||
|
moonSprite.anchor.set(0.5);
|
||||||
|
moonSprite.scale.set(0.12);
|
||||||
|
moonSprite.alpha = 0.75;
|
||||||
|
|
||||||
|
moonContainer.addChild(moonSprite);
|
||||||
|
pixiPlanetContainer.addChild(moonContainer);
|
||||||
|
pixiMoonIcons[planet.id] = moonContainer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[PixiBuilder] Rechte Planetenliste generiert.');
|
||||||
|
|
||||||
|
// Wir packen alles in ein Objekt und geben es zurück
|
||||||
|
return {
|
||||||
|
pixiPlanetLimitContainer,
|
||||||
|
pixiPlanetMask,
|
||||||
|
pixiPlanetItems,
|
||||||
|
pixiPlanetIcons,
|
||||||
|
pixiMoonIcons
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* Synchronisiert die PixiJS-Elemente kontinuierlich (bis zu 60 FPS)
|
||||||
|
* mit den absoluten Koordinaten der entsprechenden HTML-DOM-Elemente.
|
||||||
|
* Garantiert, dass die Pixi-Grafiken exakt hinter/über den HTML-Boxen liegen,
|
||||||
|
* auch wenn sich das Layout durch nachladende Schriften oder Flexbox verschiebt.
|
||||||
|
*
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz (für den Ticker)
|
||||||
|
* @param {Object} uiElements - Ein Objekt, das alle zu synchronisierenden Pixi-Container hält
|
||||||
|
*/
|
||||||
|
export function startDomSync(app, uiElements) {
|
||||||
|
const {
|
||||||
|
logoContainer,
|
||||||
|
pixiMenuMask,
|
||||||
|
pixiPlanetMask,
|
||||||
|
pixiIcons,
|
||||||
|
pixiMenuItems,
|
||||||
|
pixiPlanetItems,
|
||||||
|
pixiPlanetIcons,
|
||||||
|
pixiMoonIcons,
|
||||||
|
pixiPlanetLimitContainer
|
||||||
|
} = uiElements;
|
||||||
|
|
||||||
|
app.ticker.add(() => {
|
||||||
|
// --- Logo Synchronisation ---
|
||||||
|
if (logoContainer) {
|
||||||
|
const logoAnchor = document.getElementById('logo-anchor');
|
||||||
|
if (logoAnchor) {
|
||||||
|
const rect = logoAnchor.getBoundingClientRect();
|
||||||
|
logoContainer.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Linkes Menü (Maske) ---
|
||||||
|
if (pixiMenuMask) {
|
||||||
|
const leftMenuHtml = document.getElementById('side-menu-container');
|
||||||
|
if (leftMenuHtml) {
|
||||||
|
const rect = leftMenuHtml.getBoundingClientRect();
|
||||||
|
pixiMenuMask.clear();
|
||||||
|
pixiMenuMask.rect(rect.left, rect.top, rect.width, rect.height);
|
||||||
|
pixiMenuMask.fill(0xffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Rechtes Menü (Maske) ---
|
||||||
|
if (pixiPlanetMask) {
|
||||||
|
const rightMenuHtml = document.getElementById('right-sidebar-container');
|
||||||
|
if (rightMenuHtml) {
|
||||||
|
const rect = rightMenuHtml.getBoundingClientRect();
|
||||||
|
pixiPlanetMask.clear();
|
||||||
|
pixiPlanetMask.rect(rect.left, rect.top, rect.width, rect.height);
|
||||||
|
pixiPlanetMask.fill(0xffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Ressourcen Icons ---
|
||||||
|
for (const boxId in pixiIcons) {
|
||||||
|
const container = pixiIcons[boxId];
|
||||||
|
const htmlBox = document.getElementById(boxId);
|
||||||
|
if (htmlBox && container) {
|
||||||
|
const rect = htmlBox.getBoundingClientRect();
|
||||||
|
container.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Linke Menüpunkte (Hintergründe) ---
|
||||||
|
for (const menuId in pixiMenuItems) {
|
||||||
|
const container = pixiMenuItems[menuId];
|
||||||
|
const htmlBox = document.getElementById(menuId);
|
||||||
|
if (htmlBox && container) {
|
||||||
|
const rect = htmlBox.getBoundingClientRect();
|
||||||
|
container.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Rechte Planeten (Box-Hintergründe) ---
|
||||||
|
for (const planetId in pixiPlanetItems) {
|
||||||
|
const container = pixiPlanetItems[planetId];
|
||||||
|
const htmlBox = document.getElementById(planetId);
|
||||||
|
if (htmlBox && container) {
|
||||||
|
const rect = htmlBox.getBoundingClientRect();
|
||||||
|
container.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Rechte Planeten (Haupt-Icons) ---
|
||||||
|
for (const planetId in pixiPlanetIcons) {
|
||||||
|
const container = pixiPlanetIcons[planetId];
|
||||||
|
const htmlAnchor = document.getElementById(`planet_anchor_${planetId}`);
|
||||||
|
if (htmlAnchor && container) {
|
||||||
|
const rect = htmlAnchor.getBoundingClientRect();
|
||||||
|
container.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Rechte Planeten (Mond-Icons) ---
|
||||||
|
for (const planetId in pixiMoonIcons) {
|
||||||
|
const container = pixiMoonIcons[planetId];
|
||||||
|
const htmlAnchor = document.getElementById(`moon_anchor_${planetId}`);
|
||||||
|
if (htmlAnchor && container) {
|
||||||
|
const rect = htmlAnchor.getBoundingClientRect();
|
||||||
|
container.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Planeten-Limit Container ---
|
||||||
|
if (pixiPlanetLimitContainer) {
|
||||||
|
const limitHtmlBox = document.getElementById('planet-limit-container');
|
||||||
|
if (limitHtmlBox) {
|
||||||
|
const rect = limitHtmlBox.getBoundingClientRect();
|
||||||
|
pixiPlanetLimitContainer.position.set(
|
||||||
|
rect.left + (rect.width / 2),
|
||||||
|
rect.top + (rect.height / 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Übernimmt die Skalierungslogik für PixiJS.
|
||||||
|
* Wird jedes Mal aufgerufen, wenn der Spieler das Browserfenster vergrößert/verkleinert.
|
||||||
|
* Sorgt für einen responsiven Cover-Effekt des Hintergrundbildes.
|
||||||
|
*
|
||||||
|
* @param {Application} app - Die laufende PixiJS Instanz
|
||||||
|
* @param {Sprite} backgroundSprite - Das PixiJS-Sprite des Hintergrunds
|
||||||
|
*/
|
||||||
|
export function setupResize(app, backgroundSprite) {
|
||||||
|
function resize() {
|
||||||
|
const screenWidth = window.innerWidth;
|
||||||
|
const screenHeight = window.innerHeight;
|
||||||
|
|
||||||
|
if (backgroundSprite && backgroundSprite.texture) {
|
||||||
|
const scaleX = screenWidth / backgroundSprite.texture.width;
|
||||||
|
const scaleY = screenHeight / backgroundSprite.texture.height;
|
||||||
|
|
||||||
|
const scale = Math.max(scaleX, scaleY);
|
||||||
|
backgroundSprite.scale.set(scale);
|
||||||
|
|
||||||
|
backgroundSprite.x = (screenWidth - backgroundSprite.width) / 2;
|
||||||
|
backgroundSprite.y = (screenHeight - backgroundSprite.height) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.renderer.on('resize', resize);
|
||||||
|
resize();
|
||||||
|
}
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
@import url('./src/css/layout.css');
|
@import url('./src/css/layout.css');
|
||||||
@import url('./src/css/variables.css');
|
@import url('./src/css/variables.css');
|
||||||
@import url('./src/css/hud.css');
|
|
||||||
|
@import url('./src/components/footer/footer.css');
|
||||||
|
@import url('./src/components/topbar/topbar.css');
|
||||||
|
@import url('./src/components/resource-header/resource-header.css');
|
||||||
|
@import url('./src/components/side-menu/side-menu.css');
|
||||||
|
@import url('./src/components/planet-menu/planet-menu.css');
|
||||||
Reference in New Issue
Block a user