Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fab34b9b7 | ||
|
|
986e519545 | ||
|
|
ee9086dd16 | ||
|
|
60a34536fb | ||
|
|
4bb1180a26 | ||
|
|
df33002d30 | ||
|
|
efed783e25 | ||
|
|
54f115204f | ||
|
|
ca45f38262 | ||
|
|
ffe0c7d689 | ||
|
|
0f8c45963c | ||
|
|
e60a6b7089 | ||
|
|
3eae906b34 | ||
|
|
ea7852256f | ||
|
|
ba3de2b63b | ||
|
|
c91bf1de24 | ||
|
|
c9687af788 | ||
|
|
1be6605f8a | ||
|
|
b26ec4191a | ||
|
|
51c6867c5b | ||
|
|
e4228a01a6 | ||
|
|
4284784bf3 | ||
|
|
26225725b3 | ||
|
|
1178359927 | ||
|
|
5f2eaf326a | ||
|
|
d8bc26a75b | ||
|
|
01ba580d34 | ||
|
|
cfa5985f44 | ||
|
|
a6abe939e8 | ||
|
|
ad18d79c18 | ||
|
|
ac15ba2130 | ||
|
|
e9888acf7e | ||
|
|
6a85879f66 | ||
|
|
b84985a970 | ||
|
|
aa4b725813 | ||
|
|
a9283e32a2 | ||
|
|
310ea81d42 | ||
|
|
c4c41c00ef | ||
|
|
8afe00ebe7 | ||
|
|
c9c80f3c52 |
+3
-1
@@ -1,2 +1,4 @@
|
||||
66_plasmaTr
|
||||
export/
|
||||
91_sonnensystem
|
||||
00_aMeta/toolz/CYOA/export
|
||||
00_aMeta/toolz/CYOA/cyoa-engine
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
= Meta: System der aufsteigenden Dateien =
|
||||
:toc:
|
||||
:icons: font
|
||||
|
||||
== Das ist mein Reich, hier bin ich Gott
|
||||
|
||||
Nur wenige werden diesen Teil zu lesen bekommen also seit geehrt.
|
||||
Werde beginnen die Branches zu pflegen und die Stuktur aufbauen.
|
||||
|
||||
[cols="4*", options="header"]
|
||||
.Sichtbarkeit der Texte
|
||||
|===
|
||||
|**`sonnensystem`** |`feature/*`, `dev` |Du (Autor) |Neovim-Configs, `build.sh`, Rohnotizen, Canvas-Prototypen, Scratchpads.
|
||||
|**`galaxie`**
|
||||
|`alpha`, `testing`
|
||||
|Gast-Schreiber, Beta-Leser
|
||||
|Lore-Bibel, Styleguides, No-Go-Wortlisten, Validation-Hooks.
|
||||
|**`galaxienhaufen`**
|
||||
|`staging`, `release`
|
||||
|Lektorat, Setzer
|
||||
|Typografie-Themes, EPUB/PDF-Generatoren, Kapitel-Includes, Layout-Assets.
|
||||
|**`universum`**
|
||||
|`main` / `v1.0`
|
||||
|Öffentlichkeit, Leser
|
||||
|Kanonischer Text, Open-Source-Lizenz, Enzyklopädie/Glossar.
|
||||
|===
|
||||
|
||||
=== to be done
|
||||
|
||||
* [] Diese Datei zu jedem Branch hierher ziehen
|
||||
* [] Reposetory zu internen Gitlab Server Spiegel
|
||||
* [] Auto Build Testen
|
||||
* [] Galaxie Build Druckfahne
|
||||
@@ -0,0 +1,28 @@
|
||||
= Meta: System der aufsteigenden Dateien =
|
||||
:toc:
|
||||
:icons: font
|
||||
|
||||
== Willkomen auserwählter Leser/Schreiber
|
||||
|
||||
Hier sollten die Dinge eigendlich schon reibungslos funktionieren.
|
||||
Aber soweit unten kann es schon mal vorkommen das etwas in stocken gerät, lass dich davon nicht entmutigen.
|
||||
Frage den der dir diese Dokumente gegeben hat nach hilfe oder wo weitere zu finden ist.
|
||||
|
||||
|
||||
|
||||
[cols="4*", options="header"]
|
||||
.Sichtbarkeit der Texte
|
||||
|===
|
||||
|**`galaxie`**
|
||||
|`alpha`, `testing`
|
||||
|Gast-Schreiber, Beta-Leser
|
||||
|Lore-Bibel, Styleguides, No-Go-Wortlisten, Validation-Hooks.
|
||||
|**`galaxienhaufen`**
|
||||
|`staging`, `release`
|
||||
|Lektorat, Setzer
|
||||
|Typografie-Themes, EPUB/PDF-Generatoren, Kapitel-Includes, Layout-Assets.
|
||||
|**`universum`**
|
||||
|`main` / `v1.0`
|
||||
|Öffentlichkeit, Leser
|
||||
|Kanonischer Text, Open-Source-Lizenz, Enzyklopädie/Glossar.
|
||||
|===
|
||||
@@ -0,0 +1,22 @@
|
||||
= Meta: System der aufsteigenden Dateien =
|
||||
:toc:
|
||||
:icons: font
|
||||
|
||||
== Willkomen lieber Leser
|
||||
|
||||
die letzte Stufe vor der Öffentlichkeit, tools und Dateien werden sich dann
|
||||
dort finden wo sie erwartet werden. Druckstufe noch nicht ganz klar,
|
||||
warte auf den ersten Text der gesetzt werden kann.
|
||||
|
||||
|
||||
[cols="4*", options="header"]
|
||||
.Sichtbarkeit der Texte
|
||||
|**`galaxienhaufen`**
|
||||
|`staging`, `release`
|
||||
|Lektorat, Setzer
|
||||
|Typografie-Themes, EPUB/PDF-Generatoren, Kapitel-Includes, Layout-Assets.
|
||||
|**`universum`**
|
||||
|`main` / `v1.0`
|
||||
|Öffentlichkeit, Leser
|
||||
|Kanonischer Text, Open-Source-Lizenz, Enzyklopädie/Glossar.
|
||||
|===
|
||||
@@ -0,0 +1,22 @@
|
||||
= Meta: System der aufsteigenden Dateien =
|
||||
:toc:
|
||||
:icons: font
|
||||
|
||||
== Hallo Welt
|
||||
|
||||
Der Text hat das Ende seiner Reise reicht, ab hier kann jeder ihn lesen.
|
||||
Für fast jeden sollte etwas zum Konsomieren dabei sein.
|
||||
|
||||
[cols="4*", options="header"]
|
||||
.Sichtbarkeit der Texte
|
||||
|**`universum`**
|
||||
|`main` / `v1.0`
|
||||
|Öffentlichkeit, Leser
|
||||
|Kanonischer Text, Open-Source-Lizenz, Enzyklopädie/Glossar.
|
||||
|===
|
||||
|
||||
=== Liste der Formate
|
||||
|
||||
* webseite
|
||||
* ePub
|
||||
* PDF
|
||||
@@ -0,0 +1,641 @@
|
||||
= Design-System: Maschinen-Instanzen
|
||||
:toc:
|
||||
:toclevels: 3
|
||||
|
||||
Project X Tribut • Universelles UI-, HUD- und Grafiksystem
|
||||
|
||||
*Version:* 1.0.0 +
|
||||
*Zielplattformen:* Web, HTML5 Canvas, Browser-Games, Diagnose-Terminals, Tools, Dashboards +
|
||||
*Designsprache:* Hard-Sci-Fi • Industrial Computing • Retro-Terminal • Maschinenästhetik
|
||||
|
||||
== 1. Designphilosophie
|
||||
|
||||
Das Design-System *Maschinen-Instanzen* definiert die visuelle Sprache des gesamten Computer-Historyz-Universums.
|
||||
|
||||
Die Gestaltung orientiert sich an industriellen Steuerungssystemen, wissenschaftlichen Diagnoseterminals und kybernetischen Maschinenkomplexen. Sämtliche Farben, Formen und Effekte besitzen eine funktionale Bedeutung.
|
||||
|
||||
Die Benutzeroberfläche soll nicht dekorativ wirken, sondern wie ein Bestandteil einer realen Maschine.
|
||||
|
||||
=== Grundprinzipien
|
||||
|
||||
. Funktion vor Dekoration.
|
||||
. Jede Farbe besitzt eine semantische Bedeutung.
|
||||
. Scharfe Kanten statt organischer Formen.
|
||||
. Monospace-Typografie als Standard.
|
||||
. Technische Lesbarkeit hat Vorrang vor visuellen Effekten.
|
||||
. Informationen werden durch Farbe, Position und Kontrast vermittelt.
|
||||
. Animationen dienen der Zustandskommunikation und niemals ausschließlich der Verzierung.
|
||||
|
||||
== 2. Farbpalette
|
||||
|
||||
=== 2.1 Kernfarben
|
||||
|
||||
[cols="1,1,3",options="header"]
|
||||
|===
|
||||
|Name
|
||||
|Hex
|
||||
|Bedeutung
|
||||
|
||||
|Deep Void
|
||||
|`#05070a`
|
||||
|Globaler Hintergrund, tiefste Darstellungsebene
|
||||
|
||||
|Terminal Base
|
||||
|`#0b0e14`
|
||||
|Fenster, Container und Panel-Flächen
|
||||
|
||||
|Tech Cyan
|
||||
|`#00f2ff`
|
||||
|Interaktion, Fokus, aktive Systeme
|
||||
|
||||
|Harvest Green
|
||||
|`#00ff73`
|
||||
|Erfolg, Ressourcengewinn, Wiederherstellung
|
||||
|
||||
|Amber Gold
|
||||
|`#ffb700`
|
||||
|Warnungen, besondere Objekte, Missionsziele
|
||||
|
||||
|Critical Red
|
||||
|`#ff3e3e`
|
||||
|Kritische Zustände, Fehler, Gefahr
|
||||
|
||||
|Steel Grey
|
||||
|`#888899`
|
||||
|Sekundäre Informationen, deaktivierte Elemente
|
||||
|===
|
||||
|
||||
=== 2.2 Semantische Zustände
|
||||
|
||||
[cols="1,1",options="header"]
|
||||
|===
|
||||
|Zustand
|
||||
|Farbe
|
||||
|
||||
|Information
|
||||
|Tech Cyan
|
||||
|
||||
|Erfolg
|
||||
|Harvest Green
|
||||
|
||||
|Warnung
|
||||
|Amber Gold
|
||||
|
||||
|Kritisch
|
||||
|Critical Red
|
||||
|
||||
|Deaktiviert
|
||||
|Steel Grey
|
||||
|
||||
|Fokus
|
||||
|Tech Cyan + Glow
|
||||
|===
|
||||
|
||||
=== 2.3 Priorisierung
|
||||
|
||||
==== Primär
|
||||
|
||||
Interaktive Objekte:
|
||||
|
||||
* Buttons
|
||||
* Links
|
||||
* Menüs
|
||||
* Aktive Systeme
|
||||
|
||||
Verwenden:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Tech Cyan
|
||||
----
|
||||
|
||||
==== Sekundär
|
||||
|
||||
Nicht-interaktive Daten:
|
||||
|
||||
* Metadaten
|
||||
* Diagnostikwerte
|
||||
* Statistiken
|
||||
|
||||
Verwenden:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Steel Grey
|
||||
----
|
||||
|
||||
==== Kritisch
|
||||
|
||||
Fehler oder Gefahr:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Critical Red
|
||||
----
|
||||
|
||||
== 3. Typografie
|
||||
|
||||
=== 3.1 Schriftarten
|
||||
|
||||
Ausschließlich Monospace-Schriften.
|
||||
|
||||
Bevorzugte Reihenfolge:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
JetBrains Mono
|
||||
Fira Code
|
||||
Courier Prime
|
||||
monospace
|
||||
----
|
||||
|
||||
=== 3.2 Typografische Regeln
|
||||
|
||||
==== Beschriftungen
|
||||
|
||||
Verwendung von Kleinbuchstaben.
|
||||
|
||||
Beispiel:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
shield status
|
||||
resource storage
|
||||
diagnostics
|
||||
----
|
||||
|
||||
==== Statusmeldungen
|
||||
|
||||
Fett darstellen.
|
||||
|
||||
Beispiel:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
WARNING
|
||||
SHIELD FAILURE
|
||||
RECOVERY COMPLETE
|
||||
----
|
||||
|
||||
=== 3.3 Lesbarkeit
|
||||
|
||||
Grundregeln:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
line-height: 1.4;
|
||||
letter-spacing: 0.02em;
|
||||
----
|
||||
|
||||
Text muss stets lesbar bleiben.
|
||||
|
||||
Dekorative Effekte dürfen niemals die Lesbarkeit reduzieren.
|
||||
|
||||
=== 3.4 Text-Glow
|
||||
|
||||
Optional:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
text-shadow:
|
||||
0 0 3px rgba(0,242,255,0.3);
|
||||
----
|
||||
|
||||
Maximal:
|
||||
|
||||
* 3px Blur
|
||||
* 30 % Deckkraft
|
||||
|
||||
== 4. Layout-System
|
||||
|
||||
=== 4.1 Raster
|
||||
|
||||
Empfohlenes Grundraster:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
8px System
|
||||
----
|
||||
|
||||
Abstände erfolgen in Vielfachen von:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
8
|
||||
16
|
||||
24
|
||||
32
|
||||
48
|
||||
64
|
||||
----
|
||||
|
||||
=== 4.2 Informationshierarchie
|
||||
|
||||
Von links nach rechts:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Systemstatus
|
||||
↓
|
||||
Navigation
|
||||
↓
|
||||
Primäre Inhalte
|
||||
↓
|
||||
Kontextinformationen
|
||||
----
|
||||
|
||||
=== 4.3 Bildaufteilung
|
||||
|
||||
Für Spiele:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Links:
|
||||
Systeme / HUD
|
||||
|
||||
Mitte:
|
||||
Gameplay
|
||||
|
||||
Rechts:
|
||||
Freier Sichtbereich
|
||||
----
|
||||
|
||||
== 5. HUD-Spezifikation
|
||||
|
||||
=== 5.1 Grundkonzept
|
||||
|
||||
HUD-Elemente wirken wie physische Bestandteile der Maschine.
|
||||
|
||||
Keine schwebenden Fantasy-Elemente.
|
||||
|
||||
Keine organischen Formen.
|
||||
|
||||
=== 5.2 Statusanzeigen
|
||||
|
||||
==== Schild
|
||||
|
||||
Farbübergang:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
>60%
|
||||
Tech Cyan
|
||||
|
||||
30%-60%
|
||||
Amber Gold
|
||||
|
||||
<30%
|
||||
Critical Red
|
||||
----
|
||||
|
||||
==== Ressourcen
|
||||
|
||||
Verwendung:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Harvest Green
|
||||
----
|
||||
|
||||
==== Systemfehler
|
||||
|
||||
Verwendung:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Critical Red
|
||||
----
|
||||
|
||||
== 6. Canvas-Effekte
|
||||
|
||||
=== 6.1 Scan-Lines
|
||||
|
||||
Zweck:
|
||||
|
||||
Simulation eines industriellen Monitors.
|
||||
|
||||
Empfohlene Farbe:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
rgba(255,255,255,0.03)
|
||||
----
|
||||
|
||||
Regeln:
|
||||
|
||||
* Über der Spielwelt
|
||||
* Unter der UI
|
||||
* Dürfen Lesbarkeit nicht beeinträchtigen
|
||||
|
||||
=== 6.2 Glitch-Effekte
|
||||
|
||||
Technische Störungen werden durch kurze Cyan-Artefakte dargestellt.
|
||||
|
||||
Regeln:
|
||||
|
||||
* Maximal 150 ms sichtbar
|
||||
* Nur ereignisgesteuert
|
||||
* Keine permanente Darstellung
|
||||
|
||||
Mögliche Auslöser:
|
||||
|
||||
* Schildausfall
|
||||
* Respawn
|
||||
* Energieumschaltung
|
||||
* Speichertransfer
|
||||
* Diagnostikfehler
|
||||
|
||||
=== 6.3 Bildschirmrauschen
|
||||
|
||||
Optional:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
opacity: 0.02 - 0.04
|
||||
----
|
||||
|
||||
Niemals dominant.
|
||||
|
||||
== 7. Rahmen-System
|
||||
|
||||
=== 7.1 Grundregel
|
||||
|
||||
Vermeide klassische Betriebssystem-Fenster.
|
||||
|
||||
Stattdessen:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
┌─────────
|
||||
|
||||
└─────────
|
||||
----
|
||||
|
||||
oder Corner-Brackets.
|
||||
|
||||
=== 7.2 Rundungen
|
||||
|
||||
Nicht vorgesehen.
|
||||
|
||||
Maximal:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
border-radius: 2px;
|
||||
----
|
||||
|
||||
=== 7.3 Linien
|
||||
|
||||
Immer:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
1px
|
||||
----
|
||||
|
||||
Farbe:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Steel Grey
|
||||
----
|
||||
|
||||
Aktiv:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
Tech Cyan
|
||||
----
|
||||
|
||||
== 8. Buttons
|
||||
|
||||
=== Normal
|
||||
|
||||
[source,css]
|
||||
----
|
||||
background: transparent;
|
||||
color: #888899;
|
||||
border: 1px solid #888899;
|
||||
----
|
||||
|
||||
=== Hover
|
||||
|
||||
[source,css]
|
||||
----
|
||||
color: #00f2ff;
|
||||
border-color: #00f2ff;
|
||||
|
||||
box-shadow:
|
||||
0 0 8px #00f2ff;
|
||||
----
|
||||
|
||||
=== Aktiv
|
||||
|
||||
[source,css]
|
||||
----
|
||||
background: #00f2ff;
|
||||
color: #05070a;
|
||||
----
|
||||
|
||||
=== Disabled
|
||||
|
||||
[source,css]
|
||||
----
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
----
|
||||
|
||||
== 9. Panels und Container
|
||||
|
||||
=== Standard
|
||||
|
||||
[source,css]
|
||||
----
|
||||
background: #0b0e14;
|
||||
border: 1px solid #888899;
|
||||
|
||||
backdrop-filter: blur(4px);
|
||||
----
|
||||
|
||||
=== Overlays
|
||||
|
||||
Dürfen Schatten besitzen.
|
||||
|
||||
Beispiel:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
box-shadow:
|
||||
8px 8px 0 rgba(0,0,0,0.8);
|
||||
----
|
||||
|
||||
=== Schatten-Regel
|
||||
|
||||
Verboten:
|
||||
|
||||
* Weiche Material-Schatten
|
||||
* Neumorphe Effekte
|
||||
* Mobile-App-Schatten
|
||||
|
||||
Erlaubt:
|
||||
|
||||
* Harte technische Schatten
|
||||
|
||||
== 10. Icon-System
|
||||
|
||||
=== Stil
|
||||
|
||||
Alle Icons müssen:
|
||||
|
||||
* SVG-basiert sein
|
||||
* Geometrisch wirken
|
||||
* Scharfe Kanten besitzen
|
||||
|
||||
=== Unzulässig
|
||||
|
||||
* Airbrush
|
||||
* Illustrationen
|
||||
* Farbverläufe
|
||||
* Cartoon-Stil
|
||||
|
||||
=== Zulässig
|
||||
|
||||
* Technische Symbole
|
||||
* Schaltpläne
|
||||
* Maschinen-Piktogramme
|
||||
|
||||
== 11. Asset-Pipeline
|
||||
|
||||
=== Flächen
|
||||
|
||||
Nur Vollfarben.
|
||||
|
||||
=== Nicht erlaubt
|
||||
|
||||
* Farbverläufe
|
||||
* Weiche Lichtflächen
|
||||
* Fototexturen im UI
|
||||
|
||||
=== Ausnahmen
|
||||
|
||||
Folgende Effekte dürfen Verläufe enthalten:
|
||||
|
||||
* Glow
|
||||
* Bloom
|
||||
* Partikel
|
||||
* Plasma
|
||||
* Energieeffekte
|
||||
|
||||
== 12. Lichtsystem
|
||||
|
||||
=== Ziel
|
||||
|
||||
Licht soll technisch wirken.
|
||||
|
||||
Nicht magisch.
|
||||
|
||||
Nicht organisch.
|
||||
|
||||
Nicht dekorativ.
|
||||
|
||||
=== Glow-Regeln
|
||||
|
||||
Empfohlen:
|
||||
|
||||
[source,css]
|
||||
----
|
||||
box-shadow:
|
||||
0 0 6px currentColor;
|
||||
----
|
||||
|
||||
Eigenschaften:
|
||||
|
||||
* Kurzer Radius
|
||||
* Hoher Kontrast
|
||||
* Klar abgegrenzte Lichtquellen
|
||||
|
||||
== 13. Animationen
|
||||
|
||||
=== Erlaubt
|
||||
|
||||
* Blinken
|
||||
* Pulsieren
|
||||
* Statuswechsel
|
||||
* Diagnostik-Animationen
|
||||
* Ladeanzeigen
|
||||
|
||||
=== Nicht erlaubt
|
||||
|
||||
* Verspielte Übergänge
|
||||
* Elastische Animationen
|
||||
* Übertriebene Bewegungen
|
||||
|
||||
=== Animationsdauer
|
||||
|
||||
Empfohlen:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
100ms - 300ms
|
||||
----
|
||||
|
||||
HUD-Reaktionen:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
50ms - 150ms
|
||||
----
|
||||
|
||||
== 14. Referenz-CSS
|
||||
|
||||
[source,css]
|
||||
----
|
||||
:root {
|
||||
--bg-deep: #05070a;
|
||||
--bg-panel: #0b0e14;
|
||||
|
||||
--accent-cyan: #00f2ff;
|
||||
--accent-green: #00ff73;
|
||||
--accent-amber: #ffb700;
|
||||
--accent-red: #ff3e3e;
|
||||
|
||||
--text-dim: #888899;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-deep);
|
||||
color: var(--text-dim);
|
||||
|
||||
font-family:
|
||||
"JetBrains Mono",
|
||||
"Fira Code",
|
||||
"Courier Prime",
|
||||
monospace;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--text-dim);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: var(--accent-cyan);
|
||||
border-color: var(--accent-cyan);
|
||||
|
||||
box-shadow:
|
||||
0 0 8px var(--accent-cyan);
|
||||
}
|
||||
----
|
||||
|
||||
== Design-Leitsatz
|
||||
|
||||
*Maschinen-Instanzen ist kein futuristisches Betriebssystem.*
|
||||
|
||||
Es ist die Benutzeroberfläche einer gewaltigen industriellen Maschine, die zufällig von Menschen bedient wird.
|
||||
|
||||
Jedes Element soll vermitteln, dass unter der Oberfläche reale Energieflüsse, Diagnosesysteme, Speicherabbilder, Förderanlagen und Maschinenprozesse arbeiten.
|
||||
|
||||
Dekoration ist optional.
|
||||
|
||||
*Funktion ist Pflicht.*
|
||||
@@ -0,0 +1,648 @@
|
||||
# Design-System: Maschinen-Instanzen
|
||||
### Project X Tribut • Universelles UI-, HUD- und Grafiksystem
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Zielplattformen:** Web, HTML5 Canvas, Browser-Games, Diagnose-Terminals, Tools, Dashboards
|
||||
**Designsprache:** Hard-Sci-Fi · Industrial Computing · Retro-Terminal · Maschinenästhetik
|
||||
|
||||
---
|
||||
|
||||
# 1. Designphilosophie
|
||||
|
||||
Das Design-System *Maschinen-Instanzen* definiert die visuelle Sprache des gesamten Computer-Historyz-Universums.
|
||||
|
||||
Die Gestaltung orientiert sich an industriellen Steuerungssystemen, wissenschaftlichen Diagnoseterminals und kybernetischen Maschinenkomplexen. Sämtliche Farben, Formen und Effekte besitzen eine funktionale Bedeutung.
|
||||
|
||||
Die Benutzeroberfläche soll nicht dekorativ wirken, sondern wie ein Bestandteil einer realen Maschine.
|
||||
|
||||
## Grundprinzipien
|
||||
|
||||
1. Funktion vor Dekoration.
|
||||
2. Jede Farbe besitzt eine semantische Bedeutung.
|
||||
3. Scharfe Kanten statt organischer Formen.
|
||||
4. Monospace-Typografie als Standard.
|
||||
5. Technische Lesbarkeit hat Vorrang vor visuellen Effekten.
|
||||
6. Informationen werden durch Farbe, Position und Kontrast vermittelt.
|
||||
7. Animationen dienen der Zustandskommunikation und niemals ausschließlich der Verzierung.
|
||||
|
||||
---
|
||||
|
||||
# 2. Farbpalette
|
||||
|
||||
## 2.1 Kernfarben
|
||||
|
||||
| Name | Hex | Bedeutung |
|
||||
|--------|--------|--------|
|
||||
| Deep Void | `#05070a` | Globaler Hintergrund, tiefste Darstellungsebene |
|
||||
| Terminal Base | `#0b0e14` | Fenster, Container und Panel-Flächen |
|
||||
| Tech Cyan | `#00f2ff` | Interaktion, Fokus, aktive Systeme |
|
||||
| Harvest Green | `#00ff73` | Erfolg, Ressourcengewinn, Wiederherstellung |
|
||||
| Amber Gold | `#ffb700` | Warnungen, besondere Objekte, Missionsziele |
|
||||
| Critical Red | `#ff3e3e` | Kritische Zustände, Fehler, Gefahr |
|
||||
| Steel Grey | `#888899` | Sekundäre Informationen, deaktivierte Elemente |
|
||||
|
||||
---
|
||||
|
||||
## 2.2 Semantische Zustände
|
||||
|
||||
| Zustand | Farbe |
|
||||
|----------|----------|
|
||||
| Information | Tech Cyan |
|
||||
| Erfolg | Harvest Green |
|
||||
| Warnung | Amber Gold |
|
||||
| Kritisch | Critical Red |
|
||||
| Deaktiviert | Steel Grey |
|
||||
| Fokus | Tech Cyan + Glow |
|
||||
|
||||
---
|
||||
|
||||
## 2.3 Priorisierung
|
||||
|
||||
### Primär
|
||||
|
||||
Interaktive Objekte:
|
||||
|
||||
- Buttons
|
||||
- Links
|
||||
- Menüs
|
||||
- Aktive Systeme
|
||||
|
||||
Verwenden:
|
||||
|
||||
```text
|
||||
Tech Cyan
|
||||
```
|
||||
|
||||
### Sekundär
|
||||
|
||||
Nicht-interaktive Daten:
|
||||
|
||||
- Metadaten
|
||||
- Diagnostikwerte
|
||||
- Statistiken
|
||||
|
||||
Verwenden:
|
||||
|
||||
```text
|
||||
Steel Grey
|
||||
```
|
||||
|
||||
### Kritisch
|
||||
|
||||
Fehler oder Gefahr:
|
||||
|
||||
```text
|
||||
Critical Red
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 3. Typografie
|
||||
|
||||
## 3.1 Schriftarten
|
||||
|
||||
Ausschließlich Monospace-Schriften.
|
||||
|
||||
Bevorzugte Reihenfolge:
|
||||
|
||||
```text
|
||||
JetBrains Mono
|
||||
Fira Code
|
||||
Courier Prime
|
||||
monospace
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.2 Typografische Regeln
|
||||
|
||||
### Beschriftungen
|
||||
|
||||
Verwendung von Kleinbuchstaben.
|
||||
|
||||
Beispiel:
|
||||
|
||||
```text
|
||||
shield status
|
||||
resource storage
|
||||
diagnostics
|
||||
```
|
||||
|
||||
### Statusmeldungen
|
||||
|
||||
Fett darstellen.
|
||||
|
||||
Beispiel:
|
||||
|
||||
```text
|
||||
WARNING
|
||||
SHIELD FAILURE
|
||||
RECOVERY COMPLETE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.3 Lesbarkeit
|
||||
|
||||
Grundregeln:
|
||||
|
||||
```css
|
||||
line-height: 1.4;
|
||||
letter-spacing: 0.02em;
|
||||
```
|
||||
|
||||
Text muss stets lesbar bleiben.
|
||||
|
||||
Dekorative Effekte dürfen niemals die Lesbarkeit reduzieren.
|
||||
|
||||
---
|
||||
|
||||
## 3.4 Text-Glow
|
||||
|
||||
Optional:
|
||||
|
||||
```css
|
||||
text-shadow:
|
||||
0 0 3px rgba(0,242,255,0.3);
|
||||
```
|
||||
|
||||
Maximal:
|
||||
|
||||
- 3px Blur
|
||||
- 30% Deckkraft
|
||||
|
||||
---
|
||||
|
||||
# 4. Layout-System
|
||||
|
||||
## 4.1 Raster
|
||||
|
||||
Empfohlenes Grundraster:
|
||||
|
||||
```text
|
||||
8px System
|
||||
```
|
||||
|
||||
Abstände erfolgen in Vielfachen von:
|
||||
|
||||
```text
|
||||
8
|
||||
16
|
||||
24
|
||||
32
|
||||
48
|
||||
64
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.2 Informationshierarchie
|
||||
|
||||
Von links nach rechts:
|
||||
|
||||
```text
|
||||
Systemstatus
|
||||
↓
|
||||
Navigation
|
||||
↓
|
||||
Primäre Inhalte
|
||||
↓
|
||||
Kontextinformationen
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.3 Bildaufteilung
|
||||
|
||||
Für Spiele:
|
||||
|
||||
```text
|
||||
Links:
|
||||
Systeme / HUD
|
||||
|
||||
Mitte:
|
||||
Gameplay
|
||||
|
||||
Rechts:
|
||||
Freier Sichtbereich
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 5. HUD-Spezifikation
|
||||
|
||||
## 5.1 Grundkonzept
|
||||
|
||||
HUD-Elemente wirken wie physische Bestandteile der Maschine.
|
||||
|
||||
Keine schwebenden Fantasy-Elemente.
|
||||
|
||||
Keine organischen Formen.
|
||||
|
||||
---
|
||||
|
||||
## 5.2 Statusanzeigen
|
||||
|
||||
### Schild
|
||||
|
||||
Farbübergang:
|
||||
|
||||
```text
|
||||
>60%
|
||||
Tech Cyan
|
||||
|
||||
30%-60%
|
||||
Amber Gold
|
||||
|
||||
<30%
|
||||
Critical Red
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Ressourcen
|
||||
|
||||
Verwendung:
|
||||
|
||||
```text
|
||||
Harvest Green
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Systemfehler
|
||||
|
||||
Verwendung:
|
||||
|
||||
```text
|
||||
Critical Red
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 6. Canvas-Effekte
|
||||
|
||||
## 6.1 Scan-Lines
|
||||
|
||||
Zweck:
|
||||
|
||||
Simulation eines industriellen Monitors.
|
||||
|
||||
Empfohlene Farbe:
|
||||
|
||||
```css
|
||||
rgba(255,255,255,0.03)
|
||||
```
|
||||
|
||||
Regeln:
|
||||
|
||||
- Über der Spielwelt
|
||||
- Unter der UI
|
||||
- Dürfen Lesbarkeit nicht beeinträchtigen
|
||||
|
||||
---
|
||||
|
||||
## 6.2 Glitch-Effekte
|
||||
|
||||
Technische Störungen werden durch kurze Cyan-Artefakte dargestellt.
|
||||
|
||||
Regeln:
|
||||
|
||||
- Maximal 150 ms sichtbar
|
||||
- Nur ereignisgesteuert
|
||||
- Keine permanente Darstellung
|
||||
|
||||
Mögliche Auslöser:
|
||||
|
||||
- Schildausfall
|
||||
- Respawn
|
||||
- Energieumschaltung
|
||||
- Speichertransfer
|
||||
- Diagnostikfehler
|
||||
|
||||
---
|
||||
|
||||
## 6.3 Bildschirmrauschen
|
||||
|
||||
Optional:
|
||||
|
||||
```css
|
||||
opacity: 0.02 - 0.04
|
||||
```
|
||||
|
||||
Niemals dominant.
|
||||
|
||||
---
|
||||
|
||||
# 7. Rahmen-System
|
||||
|
||||
## 7.1 Grundregel
|
||||
|
||||
Vermeide klassische Betriebssystem-Fenster.
|
||||
|
||||
Stattdessen:
|
||||
|
||||
```text
|
||||
┌─────────
|
||||
|
||||
└─────────
|
||||
```
|
||||
|
||||
bzw. Corner-Brackets.
|
||||
|
||||
---
|
||||
|
||||
## 7.2 Rundungen
|
||||
|
||||
Nicht vorgesehen.
|
||||
|
||||
Maximal:
|
||||
|
||||
```css
|
||||
border-radius: 2px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7.3 Linien
|
||||
|
||||
Immer:
|
||||
|
||||
```css
|
||||
1px
|
||||
```
|
||||
|
||||
Farbe:
|
||||
|
||||
```text
|
||||
Steel Grey
|
||||
```
|
||||
|
||||
Aktiv:
|
||||
|
||||
```text
|
||||
Tech Cyan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 8. Buttons
|
||||
|
||||
## Normal
|
||||
|
||||
```css
|
||||
background: transparent;
|
||||
color: #888899;
|
||||
border: 1px solid #888899;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hover
|
||||
|
||||
```css
|
||||
color: #00f2ff;
|
||||
border-color: #00f2ff;
|
||||
|
||||
box-shadow:
|
||||
0 0 8px #00f2ff;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aktiv
|
||||
|
||||
```css
|
||||
background: #00f2ff;
|
||||
color: #05070a;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disabled
|
||||
|
||||
```css
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 9. Panels und Container
|
||||
|
||||
## Standard
|
||||
|
||||
```css
|
||||
background: #0b0e14;
|
||||
border: 1px solid #888899;
|
||||
|
||||
backdrop-filter: blur(4px);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Overlays
|
||||
|
||||
Dürfen Schatten besitzen.
|
||||
|
||||
Beispiel:
|
||||
|
||||
```css
|
||||
box-shadow:
|
||||
8px 8px 0 rgba(0,0,0,0.8);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Schatten-Regel
|
||||
|
||||
Verboten:
|
||||
|
||||
- weiche Material-Schatten
|
||||
- neumorphe Effekte
|
||||
- mobile App Shadows
|
||||
|
||||
Erlaubt:
|
||||
|
||||
- harte technische Schatten
|
||||
|
||||
---
|
||||
|
||||
# 10. Icon-System
|
||||
|
||||
## Stil
|
||||
|
||||
Alle Icons müssen:
|
||||
|
||||
- SVG-basiert sein
|
||||
- geometrisch wirken
|
||||
- scharfe Kanten besitzen
|
||||
|
||||
---
|
||||
|
||||
## Unzulässig
|
||||
|
||||
- Airbrush
|
||||
- Illustrationen
|
||||
- Farbverläufe
|
||||
- Cartoon-Stil
|
||||
|
||||
---
|
||||
|
||||
## Zulässig
|
||||
|
||||
- technische Symbole
|
||||
- Schaltpläne
|
||||
- Maschinen-Piktogramme
|
||||
|
||||
---
|
||||
|
||||
# 11. Asset-Pipeline
|
||||
|
||||
## Flächen
|
||||
|
||||
Nur Vollfarben.
|
||||
|
||||
---
|
||||
|
||||
## Nicht erlaubt
|
||||
|
||||
- Farbverläufe
|
||||
- weiche Lichtflächen
|
||||
- Fototexturen im UI
|
||||
|
||||
---
|
||||
|
||||
## Ausnahmen
|
||||
|
||||
Folgende Effekte dürfen Verläufe enthalten:
|
||||
|
||||
- Glow
|
||||
- Bloom
|
||||
- Partikel
|
||||
- Plasma
|
||||
- Energieeffekte
|
||||
|
||||
---
|
||||
|
||||
# 12. Lichtsystem
|
||||
|
||||
## Ziel
|
||||
|
||||
Licht soll technisch wirken.
|
||||
|
||||
Nicht magisch.
|
||||
|
||||
Nicht organisch.
|
||||
|
||||
Nicht dekorativ.
|
||||
|
||||
---
|
||||
|
||||
## Glow-Regeln
|
||||
|
||||
Empfohlen:
|
||||
|
||||
```css
|
||||
box-shadow:
|
||||
0 0 6px currentColor;
|
||||
```
|
||||
|
||||
Eigenschaften:
|
||||
|
||||
- kurzer Radius
|
||||
- hoher Kontrast
|
||||
- klar abgegrenzte Lichtquellen
|
||||
|
||||
---
|
||||
|
||||
# 13. Animationen
|
||||
|
||||
## Erlaubt
|
||||
|
||||
- Blinken
|
||||
- Pulsieren
|
||||
- Statuswechsel
|
||||
- Diagnostik-Animationen
|
||||
- Ladeanzeigen
|
||||
|
||||
---
|
||||
|
||||
## Nicht erlaubt
|
||||
|
||||
- verspielte Übergänge
|
||||
- elastische Animationen
|
||||
- übertriebene Bewegungen
|
||||
|
||||
---
|
||||
|
||||
## Animationsdauer
|
||||
|
||||
Empfohlen:
|
||||
|
||||
```text
|
||||
100ms - 300ms
|
||||
```
|
||||
|
||||
HUD-Reaktionen:
|
||||
|
||||
```text
|
||||
50ms - 150ms
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 14. Referenz-CSS
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg-deep: #05070a;
|
||||
--bg-panel: #0b0e14;
|
||||
|
||||
--accent-cyan: #00f2ff;
|
||||
--accent-green: #00ff73;
|
||||
--accent-amber: #ffb700;
|
||||
--accent-red: #ff3e3e;
|
||||
|
||||
--text-dim: #888899;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-deep);
|
||||
color: var(--text-dim);
|
||||
|
||||
font-family:
|
||||
"JetBrains Mono",
|
||||
"Fira Code",
|
||||
"Courier Prime",
|
||||
monospace;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--text-dim);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: var(--accent-cyan);
|
||||
border-color: var(--accent-cyan);
|
||||
|
||||
box-shadow:
|
||||
0 0 8px var(--accent-cyan);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Design-Leitsatz
|
||||
|
||||
**Maschinen-Instanzen ist kein futuristisches Betriebssystem.**
|
||||
|
||||
Es ist die Benutzeroberfläche einer gewaltigen industriellen Maschine, die zufällig von Menschen bedient wird.
|
||||
|
||||
Jedes Element soll vermitteln, dass unter der Oberfläche reale Energieflüsse, Diagnosesysteme, Speicherabbilder, Förderanlagen und Maschinenprozesse arbeiten.
|
||||
|
||||
Dekoration ist optional.
|
||||
|
||||
Funktion ist Pflicht.
|
||||
@@ -0,0 +1,259 @@
|
||||
= REVERSE GAME DESIGN DOCUMENT (GDD)
|
||||
:toc: left
|
||||
:toclevels: 3
|
||||
|
||||
Projekt: Maschinen-Instanzen – Asteroiden-Sektor (Project X Tribut)
|
||||
|
||||
*Version:* 1.0.0 +
|
||||
*Genre:* Hard-Sci-Fi / Retro-Shmup (2.5D) +
|
||||
*Setting:* Das „Computer Historyz“-Universum (Sektor: Oortsche Wolke 42)
|
||||
|
||||
== 1. Vision & Kernkonzept
|
||||
|
||||
Ein endloses, horizontales Shoot-'em-up in 2D, das innerhalb einer echten 3D-Umgebung gerendert wird.
|
||||
|
||||
Der Spieler steuert eine kybernetische Ernte-Instanz (Bergbau-Schiff), die am Rand eines gigantischen, planetengroßen Maschinen-Ansaugstutzens operiert.
|
||||
|
||||
Das Spiel kombiniert rasante Arcade-Asteroiden-Zerstörung mit taktischem Power-Routing (Energie-Management) unter harten Hard-Sci-Fi-Rahmenbedingungen.
|
||||
|
||||
== 2. Globale Farbpalette & Ästhetik
|
||||
|
||||
Diese Farbpalette definiert das visuelle Leitbild für alle zukünftigen Spiele in diesem Universum, unabhängig davon, ob sie als Pixel-Art-, Vektor- oder PBR-basierte 3D-Titel umgesetzt werden.
|
||||
|
||||
=== 2.1 UI-, HUD- und Terminal-Farben (Window-Maker-Stil)
|
||||
|
||||
Die Benutzeroberfläche basiert auf dem kalten, funktionalen Look klassischer Unix-Terminals, kombiniert mit modernen Glow-Effekten.
|
||||
|
||||
[cols="1,1,3",options="header"]
|
||||
|===
|
||||
|Farbe
|
||||
|Hex-Code
|
||||
|Semantischer Nutzen
|
||||
|
||||
|Deep Void
|
||||
|`#05070a`
|
||||
|Globaler Weltraum-Hintergrund, tiefste Schatten.
|
||||
|
||||
|Terminal Base
|
||||
|`#0d1117`
|
||||
|Hintergrund für Menüs, HUD-Panels und Dialogfenster.
|
||||
|
||||
|Panel Border
|
||||
|`#444c56`
|
||||
|Rahmen von UI-Fenstern, häufig kombiniert mit inneren Highlights für 3D-Bevel-Effekte.
|
||||
|
||||
|Tech Cyan
|
||||
|`#58a6ff`
|
||||
|Primäre Akzentfarbe. Standard-Text, normale Laser, Hologramme, aktiver Schild (HP > 60 %) und UI-Glow.
|
||||
|
||||
|Harvest Green
|
||||
|`#39d353`
|
||||
|Erfolgs-Feedback, gesammelte Ressourcen und Laser-Stufe 2.
|
||||
|
||||
|Amber Gold
|
||||
|`#d29922`
|
||||
|Warnungen, Highscores, wertvolle Materialien, Schild (HP < 60 %) und Laser-Stufe 3.
|
||||
|
||||
|Critical Red
|
||||
|`#f85149`
|
||||
|Lebensgefahr, kritischer Schild (HP < 30 %), Triebwerks-Exhaust und Laser-Stufe 4+.
|
||||
|===
|
||||
|
||||
=== 2.2 Materialfarben (3D-Modelle und 2D-Sprites)
|
||||
|
||||
Für gerenderte 3D-Objekte und gezeichnete 2D-Sprites gelten folgende Materialdefinitionen:
|
||||
|
||||
[cols="1,1,2,2",options="header"]
|
||||
|===
|
||||
|Element
|
||||
|Farbe
|
||||
|3D-Eigenschaften (PBR)
|
||||
|2D-Eigenschaften
|
||||
|
||||
|Maschinen-Hülle
|
||||
|`#1f6feb` (Blau-Metallic)
|
||||
|Roughness: 0.4, Metalness: 0.8
|
||||
|Satte dunkle Blautöne mit hartem weißen Specular-Highlight.
|
||||
|
||||
|Triebwerke / Rohre
|
||||
|`#21262d` (Dunkelgrau)
|
||||
|Roughness: 0.9, Metalness: 1.0
|
||||
|Nahezu schwarze Flächen mit hohem Kontrast.
|
||||
|
||||
|Eis-Asteroid
|
||||
|`#d0e8ff` (Blass-Eisblau)
|
||||
|Roughness: 0.9, Metalness: 0.1
|
||||
|Helle Töne, geringe Schattenbildung, weiße Staubpartikel.
|
||||
|
||||
|Fels-Asteroid
|
||||
|`#6b4a31` (Braun)
|
||||
|Roughness: 1.0, Metalness: 0.1
|
||||
|Matte, strukturierte und körnige Oberflächen.
|
||||
|
||||
|Eisen-Asteroid
|
||||
|`#888899` (Grau/Silber)
|
||||
|Roughness: 0.3, Metalness: 0.9
|
||||
|Spiegelnde Oberflächen mit deutlich sichtbaren Glanzlichtern.
|
||||
|===
|
||||
|
||||
== 3. Gameplay & Mechaniken
|
||||
|
||||
=== 3.1 Das Plasma-Blasen-Paradoxon (Movement)
|
||||
|
||||
. *Lore-Hintergrund:*
|
||||
Das Schiff erzeugt ein Magnetfeld zum Sammeln von Weltraumstaub. Dadurch entsteht eine künstliche Atmosphäre im Vakuum.
|
||||
|
||||
. *Mechanik:*
|
||||
Das Schiff wird aerodynamisch gesteuert. Es fliegt nicht auf festen Schienen, sondern neigt sich beim Steigen und Sinken entlang einer simulierten Roll-/Yaw-Bewegung.
|
||||
|
||||
. *Feedback:*
|
||||
Die Plasma-Blase pulsiert visuell. Mit steigender Schildenergie wächst ihre Größe von 0.6× bis 1.5× der Schiffsausdehnung.
|
||||
|
||||
=== 3.2 Taktisches Power-Routing (Energie-Management)
|
||||
|
||||
Die zentrale Hard-Sci-Fi-Mechanik basiert auf zwei Ressourcen:
|
||||
|
||||
* *Schild (Defensive)*
|
||||
* *Laser / Score (Offensive)*
|
||||
|
||||
Der Spieler kann Energie in Echtzeit zwischen beiden Systemen umverteilen.
|
||||
|
||||
. *Schild laden (Defensive):*
|
||||
Der Spieler opfert Score beziehungsweise Laserstufen, um den Schild wiederherzustellen.
|
||||
|
||||
. *Laser pushen (Offensive):*
|
||||
Energie wird dem Schild entzogen und in die Waffensysteme umgeleitet.
|
||||
|
||||
. *Visuelle Regel:*
|
||||
Dieses Risiko-Belohnungs-System muss in jeder Umsetzung über die Farbskala von `Tech Cyan` bis `Critical Red` dargestellt werden.
|
||||
|
||||
=== 3.3 Asteroiden-Split-Physik & Materialklassen
|
||||
|
||||
Gegnerische Asteroiden teilen sich beim Zerstören in zwei kleinere und schnellere Fragmente.
|
||||
|
||||
. *Eis (Level 1-2):*
|
||||
1× Basis-HP. Schnell, aber äußerst fragil.
|
||||
|
||||
. *Gestein (Level 2-4):*
|
||||
2× Basis-HP. Standardgegner.
|
||||
|
||||
. *Eisen (Level 3-5):*
|
||||
4× Basis-HP. Massive Brocken mit dreifachem Scorewert. Schwache Laser werden vollständig blockiert.
|
||||
|
||||
=== 3.4 Das Cylon-Recovery-System (Game Over / Respawn)
|
||||
|
||||
. *Mechanik:*
|
||||
Es existiert kein endgültiger Tod. Beim Verlust der Hülle wird das Speicherabbild gesichert.
|
||||
|
||||
. *Ablauf:*
|
||||
Das Spiel pausiert.
|
||||
|
||||
Ein Diagnostik-Terminal meldet:
|
||||
|
||||
[source,text]
|
||||
----
|
||||
DIAGNOSTICS
|
||||
Hull Destroyed
|
||||
----
|
||||
|
||||
Ein roter Fortschrittsbalken füllt sich, während das Bewusstsein in eine neue Instanz übertragen wird.
|
||||
|
||||
Der Spieler behält Laser-Level und Langzeitfortschritt, verliert jedoch Zeit sowie laufende Multiplikatoren.
|
||||
|
||||
== 4. Räumliche Komposition (Kamera & Level-Architektur)
|
||||
|
||||
=== 4.1 Der C-Rahmen (Die Maschine)
|
||||
|
||||
. *Konzept:*
|
||||
Der Spieler operiert nicht im leeren Raum. Die linke Bildschirmseite wird von der gewaltigen Hauptmaschine eingenommen.
|
||||
|
||||
. *Layout:*
|
||||
Das Mahlwerk erscheint als massive Struktur im oberen und unteren linken Bildbereich.
|
||||
|
||||
* 3D: Box-Geometry
|
||||
* 2D: Parallax-Layer
|
||||
|
||||
. *Der Korridor:*
|
||||
Die mittlere Zone der linken Bildschirmhälfte bleibt frei und garantiert Sicht auf das Spielgeschehen.
|
||||
|
||||
. *Reaktivität:*
|
||||
Trümmerfragmente werden nach links in den Schlund der Maschine gesaugt.
|
||||
|
||||
Mit zunehmender Materialzufuhr:
|
||||
|
||||
* Rotieren die Häcksler schneller.
|
||||
* Verstärkt sich der Plasma-Halo.
|
||||
* Steigt die visuelle Aktivität der Anlage.
|
||||
|
||||
=== 4.2 UI-Platzierung (HUD)
|
||||
|
||||
Das HUD integriert sich in den physischen C-Rahmen der Maschine.
|
||||
|
||||
. *Oben links:*
|
||||
Schiff-Diagnostik (Schild, Laser, Statuswerte).
|
||||
|
||||
. *Unten links:*
|
||||
Ressourcenanzeige (Punkte, Highscore, Materialzähler).
|
||||
|
||||
. *Ziel:*
|
||||
Die rechte und zentrale Bildhälfte bleiben vollständig für das eigentliche Spielgeschehen reserviert.
|
||||
|
||||
== 5. Technische Struktur (Code-Architektur)
|
||||
|
||||
Für Programmierer definiert die Konfiguration `GAME_CONFIG` die zentrale Wahrheitsquelle.
|
||||
|
||||
Magic Numbers im Quellcode sind nicht zulässig.
|
||||
|
||||
Jede Umsetzung des Spiels muss mindestens folgende Strukturobjekte enthalten:
|
||||
|
||||
. `world`
|
||||
+
|
||||
Konstanten für Scrollgeschwindigkeit, Zeitskalierung und globale Simulation.
|
||||
|
||||
. `player`
|
||||
+
|
||||
Geschwindigkeit, Kollisionsradius und Basiswerte des Spielerschiffs.
|
||||
|
||||
. `laser`
|
||||
+
|
||||
Feuerrate, Schaden und Upgrade-Grenzen (Level 1-5).
|
||||
|
||||
. `asteroidTypes`
|
||||
+
|
||||
Dictionary zur Zuordnung von:
|
||||
|
||||
* Eis
|
||||
* Gestein
|
||||
* Eisen
|
||||
|
||||
mit:
|
||||
|
||||
* HP-Multiplikatoren
|
||||
* Asset-Pfaden
|
||||
* Sprite-Dateien (`.png`)
|
||||
* 3D-Modellen (`.glb`)
|
||||
|
||||
. `levelWaves`
|
||||
+
|
||||
Wahrscheinlichkeitsmatrix zur Definition der Spawn-Verteilung von Asteroidentypen innerhalb der Progression.
|
||||
|
||||
|
||||
=== Zusammenfassung des erstellten Spiels
|
||||
|
||||
. *Achsengerechtes Gameplay (Horizontal Scroll Shmup):*
|
||||
Du navigierst dein Schiff auf der linken Seite des Bildschirms, während Asteroiden aus dem scheinbar unendlichen interstellaren Vakuum auf dich zurasen.
|
||||
|
||||
. *Kombiniertes 3D im Browser:*
|
||||
Sämtliche Raumschiffkomponenten (Düse, Triebwerke, Flügel) sowie die unregelmäßig geformten Asteroiden werden dynamisch per Code in einer 3D-Umgebung erzeugt, gerendert und manipuliert.
|
||||
|
||||
. *Plasma-Blasen-Physik:*
|
||||
Um das Schiff pulsiert ein transparentes Energiefeld, das den in der Lore beschriebenen Strömungswiderstand simuliert und die besondere Flugcharakteristik der Ernte-Instanzen nachvollziehbar macht.
|
||||
|
||||
. *Asteroids-Split-Mechanik:*
|
||||
Große Asteroiden zerbrechen bei Beschuss kinetisch korrekt in zwei kleinere und schnellere Fragmente. Diese müssen wiederum zerstört werden, um wertvolle Rohstoffe zu bergen.
|
||||
|
||||
. *Cylonen-Reboot-System:*
|
||||
Wird die Schiffshülle zerstört, erscheint ein retro-futuristisches Cylon-Interface mit Diagnose- und Transpositionsmeldungen. Anschließend wird das unsterbliche Speicherabbild in ein neues Chassis übertragen.
|
||||
|
||||
. *Web-Audio-Synthesizer:*
|
||||
Sämtliche Soundeffekte werden in Echtzeit synthetisiert. Dazu gehören Laser-Frequenzrampen, tieffrequente Asteroidenexplosionen sowie das charakteristische Aufheulen des Reboot-Vorgangs.
|
||||
@@ -0,0 +1,103 @@
|
||||
# REVERSE GAME DESIGN DOCUMENT (GDD)
|
||||
|
||||
**Projekt:** Maschinen-Instanzen – Asteroiden-Sektor (Project X Tribut)
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Genre:** Hard-Sci-Fi / Retro-Shmup (2.5D)
|
||||
**Setting:** Das „Computer Historyz“-Universum (Sektor: Oortsche Wolke 42)
|
||||
|
||||
## 1. Vision & Kernkonzept
|
||||
|
||||
Ein endloses, horizontales Shoot-'em-up in 2D, das innerhalb einer echten 3D-Umgebung gerendert wird.
|
||||
|
||||
Der Spieler steuert eine kybernetische Ernte-Instanz (Bergbau-Schiff), die am Rand eines gigantischen, planetengroßen Maschinen-Ansaugstutzens operiert.
|
||||
|
||||
Das Spiel kombiniert rasante Arcade-Asteroiden-Zerstörung mit taktischem Power-Routing (Energie-Management) unter harten Hard-Sci-Fi-Rahmenbedingungen.
|
||||
|
||||
## 2. Globale Farbpalette & Ästhetik
|
||||
|
||||
Diese Farbpalette definiert das visuelle Leitbild für alle zukünftigen Spiele in diesem Universum, unabhängig davon, ob sie als Pixel-Art-, Vektor- oder PBR-basierte 3D-Titel umgesetzt werden.
|
||||
|
||||
### 2.1 UI-, HUD- und Terminal-Farben (Window-Maker-Stil)
|
||||
|
||||
Die Benutzeroberfläche basiert auf dem kalten, funktionalen Look klassischer Unix-Terminals, kombiniert mit modernen Glow-Effekten.
|
||||
|
||||
| Farbe | Hex-Code | Semantischer Nutzen |
|
||||
|---------|---------|---------|
|
||||
| Deep Void | `#05070a` | Globaler Weltraum-Hintergrund, tiefste Schatten. |
|
||||
| Terminal Base | `#0d1117` | Hintergrund für Menüs, HUD-Panels und Dialogfenster. |
|
||||
| Panel Border | `#444c56` | Rahmen von UI-Fenstern, häufig kombiniert mit inneren Highlights für 3D-Bevel-Effekte. |
|
||||
| Tech Cyan | `#58a6ff` | Primäre Akzentfarbe. Standard-Text, normale Laser, Hologramme, aktiver Schild (HP > 60 %) und UI-Glow. |
|
||||
| Harvest Green | `#39d353` | Erfolgs-Feedback, gesammelte Ressourcen und Laser-Stufe 2. |
|
||||
| Amber Gold | `#d29922` | Warnungen, Highscores, wertvolle Materialien, Schild (HP < 60 %) und Laser-Stufe 3. |
|
||||
| Critical Red | `#f85149` | Lebensgefahr, kritischer Schild (HP < 30 %), Triebwerks-Exhaust und Laser-Stufe 4+. |
|
||||
|
||||
### 2.2 Materialfarben (3D-Modelle und 2D-Sprites)
|
||||
|
||||
Für gerenderte 3D-Objekte und gezeichnete 2D-Sprites gelten folgende Materialdefinitionen:
|
||||
|
||||
| Element | Farbe | 3D-Eigenschaften (PBR) | 2D-Eigenschaften |
|
||||
|---------|---------|---------|---------|
|
||||
| Maschinen-Hülle | `#1f6feb` (Blau-Metallic) | Roughness: 0.4, Metalness: 0.8 | Satte dunkle Blautöne mit hartem weißen Specular-Highlight |
|
||||
| Triebwerke / Rohre | `#21262d` (Dunkelgrau) | Roughness: 0.9, Metalness: 1.0 | Nahezu schwarze Flächen mit hohem Kontrast |
|
||||
| Eis-Asteroid | `#d0e8ff` (Blass-Eisblau) | Roughness: 0.9, Metalness: 0.1 | Helle Töne, geringe Schattenbildung, weiße Staubpartikel |
|
||||
| Fels-Asteroid | `#6b4a31` (Braun) | Roughness: 1.0, Metalness: 0.1 | Matte, strukturierte und körnige Oberflächen |
|
||||
| Eisen-Asteroid | `#888899` (Grau/Silber) | Roughness: 0.3, Metalness: 0.9 | Spiegelnde Oberflächen mit deutlich sichtbaren Glanzlichtern |
|
||||
|
||||
## 3. Gameplay & Mechaniken
|
||||
|
||||
### 3.1 Das Plasma-Blasen-Paradoxon (Movement)
|
||||
|
||||
1. **Lore-Hintergrund:**
|
||||
Das Schiff erzeugt ein Magnetfeld zum Sammeln von Weltraumstaub. Dadurch entsteht eine künstliche Atmosphäre im Vakuum.
|
||||
|
||||
2. **Mechanik:**
|
||||
Das Schiff wird aerodynamisch gesteuert. Es fliegt nicht auf festen Schienen, sondern neigt sich beim Steigen und Sinken entlang einer simulierten Roll-/Yaw-Bewegung.
|
||||
|
||||
3. **Feedback:**
|
||||
Die Plasma-Blase pulsiert visuell. Mit steigender Schildenergie wächst ihre Größe von 0.6× bis 1.5× der Schiffsausdehnung.
|
||||
|
||||
### 3.2 Taktisches Power-Routing (Energie-Management)
|
||||
|
||||
Die zentrale Hard-Sci-Fi-Mechanik basiert auf zwei Ressourcen:
|
||||
|
||||
- **Schild (Defensive)**
|
||||
- **Laser / Score (Offensive)**
|
||||
|
||||
Der Spieler kann Energie in Echtzeit zwischen beiden Systemen umverteilen.
|
||||
|
||||
1. **Schild laden (Defensive):**
|
||||
Der Spieler opfert Score beziehungsweise Laserstufen, um den Schild wiederherzustellen.
|
||||
|
||||
2. **Laser pushen (Offensive):**
|
||||
Energie wird dem Schild entzogen und in die Waffensysteme umgeleitet.
|
||||
|
||||
3. **Visuelle Regel:**
|
||||
Dieses Risiko-Belohnungs-System muss in jeder Umsetzung über die Farbskala von `Tech Cyan` bis `Critical Red` dargestellt werden.
|
||||
|
||||
### 3.3 Asteroiden-Split-Physik & Materialklassen
|
||||
|
||||
Gegnerische Asteroiden teilen sich beim Zerstören in zwei kleinere und schnellere Fragmente.
|
||||
|
||||
1. **Eis (Level 1-2):**
|
||||
1× Basis-HP. Schnell, aber äußerst fragil.
|
||||
|
||||
2. **Gestein (Level 2-4):**
|
||||
2× Basis-HP. Standardgegner.
|
||||
|
||||
3. **Eisen (Level 3-5):**
|
||||
4× Basis-HP. Massive Brocken mit dreifachem Scorewert. Schwache Laser werden vollständig blockiert.
|
||||
|
||||
### 3.4 Das Cylon-Recovery-System (Game Over / Respawn)
|
||||
|
||||
1. **Mechanik:**
|
||||
Es existiert kein endgültiger Tod. Beim Verlust der Hülle wird das Speicherabbild gesichert.
|
||||
|
||||
2. **Ablauf:**
|
||||
Das Spiel pausiert.
|
||||
|
||||
Ein Diagnostik-Terminal meldet:
|
||||
|
||||
```text
|
||||
DIAGNOSTICS
|
||||
Hull Destroyed
|
||||
@@ -0,0 +1,20 @@
|
||||
FROM debian:trixie-slim
|
||||
|
||||
# Asciidoctor installieren und apt-Cache sofort wieder leeren, um das Image klein zu halten
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends asciidoctor && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd --gid 1000 flammenderberg && \
|
||||
useradd --uid 1000 --gid flammenderberg --shell /bin/bash --create-home hexenmeister
|
||||
|
||||
WORKDIR /weltraum
|
||||
RUN mkdir stories && mkdir export && mkdir frontend
|
||||
COPY ./cyoa-engine ./
|
||||
COPY ./frontend ./frontend/
|
||||
|
||||
RUN chown -R hexenmeister:flammenderberg /weltraum
|
||||
|
||||
EXPOSE 3344
|
||||
|
||||
CMD ["/weltraum/cyoa-engine", "dir ./stories/", "serve", "port 3344"]
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
|
||||
# baue das go backend
|
||||
|
||||
echo " ### Cast the caldron into a mold"
|
||||
go build -x -o cyoa-engine main.go
|
||||
echo -e
|
||||
|
||||
# baue das docker image für die welt
|
||||
|
||||
echo " ### create Firetop Mountain"
|
||||
docker build -t asciidoctor-echo -f Dockerfile.echobox .
|
||||
echo -e
|
||||
|
||||
# starte den docker container
|
||||
|
||||
echo " ### der hexenmeister zum flammenden berg"
|
||||
docker run --rm -v "$(pwd)/stories:/weltraum/stories" -v "$(pwd)/export:/weltraum/export" -p "6699:3344" asciidoctor-echo ./cyoa-engine -dir ./stories/ -serve -port 3344
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// engine_logic.js - Reine State- und Datenverarbeitung (Kein UI, Kein Fetch)
|
||||
const EngineLogic = {
|
||||
storyData: { nodes: {} },
|
||||
state: { reboots: 1, node: "" },
|
||||
|
||||
init: function(dataObj) {
|
||||
if (!dataObj || !dataObj.nodes || Object.keys(dataObj.nodes).length === 0) {
|
||||
console.warn("[System] Ungültige Graph-Daten. Fallback geladen.");
|
||||
this.storyData = {
|
||||
startNodeId: "err",
|
||||
nodes: {
|
||||
"err": {
|
||||
id: "err", title: "SYSTEM OFFLINE",
|
||||
content: "<p>Konnte <code>graph.json</code> nicht laden.</p>",
|
||||
choices: [],
|
||||
metadata: { status: "offline" }
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
this.storyData = dataObj;
|
||||
}
|
||||
|
||||
this.state.node = this.storyData.startNodeId || Object.keys(this.storyData.nodes)[0] || "";
|
||||
},
|
||||
|
||||
getNode: function(id) { return this.storyData.nodes[id]; },
|
||||
|
||||
processNodeEntry: function(id) {
|
||||
const node = this.getNode(id);
|
||||
if (!node) return null;
|
||||
|
||||
this.state.node = id;
|
||||
|
||||
if (node.metadata) {
|
||||
Object.keys(node.metadata).forEach(key => {
|
||||
this.state[key] = node.metadata[key];
|
||||
});
|
||||
}
|
||||
|
||||
if (node.isReboot) this.state.reboots++;
|
||||
return node;
|
||||
},
|
||||
|
||||
parseRequirement: function(choiceText) {
|
||||
let conditionMatch = choiceText.match(/\[req:\s*([a-zA-Z0-9_]+)\s*=\s*([^\]]+)\]/i);
|
||||
let displayText = choiceText;
|
||||
let meetsCondition = true;
|
||||
let missingReq = "";
|
||||
|
||||
if (conditionMatch) {
|
||||
let reqKey = conditionMatch[1].toLowerCase();
|
||||
let reqVal = conditionMatch[2];
|
||||
if (String(this.state[reqKey]) !== String(reqVal)) {
|
||||
meetsCondition = false;
|
||||
missingReq = `(Benötigt: ${reqKey}=${reqVal})`;
|
||||
}
|
||||
displayText = displayText.replace(conditionMatch[0], "").trim();
|
||||
}
|
||||
|
||||
return { displayText, meetsCondition, missingReq };
|
||||
},
|
||||
|
||||
triggerReboot: function() {
|
||||
const currentReboots = this.state.reboots + 1;
|
||||
this.state = { reboots: currentReboots };
|
||||
return this.storyData.startNodeId;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
// engine_ui.js - Statisches Rendering & asynchrones Nachladen der graph.json
|
||||
const EngineUI = {
|
||||
audioCtx: null,
|
||||
|
||||
init: async function() {
|
||||
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
if(AudioContext) this.audioCtx = new AudioContext();
|
||||
|
||||
// 1. Asynchroner Fetch der graph.json
|
||||
// (Im Dev-Modus liefert Go dies on-the-fly, im Export-Modus ist es eine Datei)
|
||||
try {
|
||||
const response = await fetch('graph.json', { cache: "no-store" });
|
||||
if (!response.ok) throw new Error("HTTP Fehler " + response.status);
|
||||
const graphData = await response.json();
|
||||
|
||||
// 2. Initialisiere die gekapselte Logik mit den frisch geladenen Daten
|
||||
EngineLogic.init(graphData);
|
||||
} catch (err) {
|
||||
console.error("Fehler beim Abrufen der graph.json:", err);
|
||||
EngineLogic.init(null);
|
||||
}
|
||||
|
||||
this.attachEvents();
|
||||
this.renderNode(EngineLogic.state.node);
|
||||
},
|
||||
|
||||
playBeep: function(freq = 440, type = "square", duration = 0.05) {
|
||||
try {
|
||||
if(!this.audioCtx) return;
|
||||
const osc = this.audioCtx.createOscillator();
|
||||
const gain = this.audioCtx.createGain();
|
||||
osc.type = type;
|
||||
osc.frequency.setValueAtTime(freq, this.audioCtx.currentTime);
|
||||
gain.gain.setValueAtTime(0.05, this.audioCtx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, this.audioCtx.currentTime + duration);
|
||||
osc.connect(gain); gain.connect(this.audioCtx.destination);
|
||||
osc.start(); osc.stop(this.audioCtx.currentTime + duration);
|
||||
} catch(e) {}
|
||||
},
|
||||
|
||||
renderHUD: function() {
|
||||
const hud = document.getElementById("hud-dynamic");
|
||||
hud.innerHTML = "";
|
||||
|
||||
hud.innerHTML += `<div class="stat-item">REBOOTS: <span style="color:var(--term-cyan)">#${String(EngineLogic.state.reboots).padStart(3, "0")}</span></div>`;
|
||||
hud.innerHTML += `<div class="stat-item">KNOTEN: <span style="color:var(--term-amber)">${EngineLogic.state.node}</span></div>`;
|
||||
|
||||
Object.keys(EngineLogic.state).forEach(key => {
|
||||
if(key !== "reboots" && key !== "node") {
|
||||
hud.innerHTML += `<div class="stat-item">${key.toUpperCase()}: <span style="color:var(--term-green)">${EngineLogic.state[key]}</span></div>`;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderNode: function(id) {
|
||||
const node = EngineLogic.processNodeEntry(id);
|
||||
if (!node) return;
|
||||
|
||||
this.playBeep(600, "sine", 0.04);
|
||||
if (node.isReboot) this.playBeep(200, "sawtooth", 0.3);
|
||||
|
||||
this.renderHUD();
|
||||
|
||||
document.getElementById("node-title").innerText = "=== " + node.title + " ===";
|
||||
document.getElementById("node-body").innerHTML = node.content;
|
||||
|
||||
const choicesBox = document.getElementById("choices-box");
|
||||
choicesBox.innerHTML = "";
|
||||
|
||||
if (node.choices && node.choices.length > 0) {
|
||||
let visibleIndex = 1;
|
||||
node.choices.forEach((choice) => {
|
||||
const parsed = EngineLogic.parseRequirement(choice.text);
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "choice-btn";
|
||||
|
||||
if (parsed.meetsCondition) {
|
||||
btn.innerHTML = `<span class="choice-key">[${visibleIndex}]</span> ${parsed.displayText}`;
|
||||
btn.dataset.target = choice.targetId;
|
||||
btn.dataset.index = visibleIndex;
|
||||
btn.onclick = () => this.renderNode(choice.targetId);
|
||||
visibleIndex++;
|
||||
} else {
|
||||
btn.innerHTML = `<span class="choice-key">[LOCKED]</span> ${parsed.displayText} <span style="color:var(--term-warn); font-size:0.8rem; margin-left:auto;">${parsed.missingReq}</span>`;
|
||||
btn.disabled = true;
|
||||
}
|
||||
choicesBox.appendChild(btn);
|
||||
});
|
||||
} else {
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "choice-btn";
|
||||
btn.style.borderColor = "var(--term-amber)";
|
||||
btn.style.color = "var(--term-amber)";
|
||||
btn.innerHTML = "<span class=\"choice-key\">[R]</span> Speicherabbild sichern und rebooten...";
|
||||
btn.onclick = () => this.triggerReboot();
|
||||
choicesBox.appendChild(btn);
|
||||
}
|
||||
document.getElementById("terminal-viewport").scrollTop = 0;
|
||||
},
|
||||
|
||||
triggerReboot: function() {
|
||||
const startId = EngineLogic.triggerReboot();
|
||||
if (startId) this.renderNode(startId);
|
||||
},
|
||||
|
||||
toggleGraph: function() {
|
||||
const modal = document.getElementById("graph-modal");
|
||||
if (modal.style.display === "flex") {
|
||||
modal.style.display = "none";
|
||||
} else {
|
||||
modal.style.display = "flex";
|
||||
this.drawGraphCanvas();
|
||||
}
|
||||
},
|
||||
|
||||
drawGraphCanvas: function() {
|
||||
const canvas = document.getElementById("graph-canvas");
|
||||
canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.fillStyle = "#000"; ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const keys = Object.keys(EngineLogic.storyData.nodes || {});
|
||||
if (keys.length === 0) return;
|
||||
|
||||
const total = keys.length; const radius = Math.min(canvas.width, canvas.height) * 0.38;
|
||||
const centerX = canvas.width / 2; const centerY = canvas.height / 2;
|
||||
const nodeRadius = 8; const positions = {};
|
||||
|
||||
keys.forEach((key, idx) => {
|
||||
const angle = (idx / total) * 2 * Math.PI;
|
||||
positions[key] = { x: centerX + radius * Math.cos(angle), y: centerY + radius * Math.sin(angle) };
|
||||
});
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
keys.forEach(key => {
|
||||
const node = EngineLogic.storyData.nodes[key];
|
||||
if (node && node.choices) {
|
||||
node.choices.forEach(c => {
|
||||
if (positions[c.targetId]) {
|
||||
const start = positions[key], end = positions[c.targetId];
|
||||
ctx.beginPath(); ctx.strokeStyle = "rgba(51, 255, 102, 0.4)";
|
||||
ctx.moveTo(start.x, start.y); ctx.lineTo(end.x, end.y); ctx.stroke();
|
||||
const dx = end.x - start.x, dy = end.y - start.y, angle = Math.atan2(dy, dx), headlen = 12;
|
||||
const tipX = end.x - Math.cos(angle) * (nodeRadius + 2), tipY = end.y - Math.sin(angle) * (nodeRadius + 2);
|
||||
ctx.beginPath(); ctx.moveTo(tipX, tipY);
|
||||
ctx.lineTo(tipX - headlen * Math.cos(angle - Math.PI / 7), tipY - headlen * Math.sin(angle - Math.PI / 7));
|
||||
ctx.lineTo(tipX - headlen * Math.cos(angle + Math.PI / 7), tipY - headlen * Math.sin(angle + Math.PI / 7));
|
||||
ctx.closePath(); ctx.fillStyle = "rgba(51, 255, 102, 0.9)"; ctx.fill();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
keys.forEach(key => {
|
||||
const pos = positions[key];
|
||||
ctx.beginPath(); ctx.arc(pos.x, pos.y, nodeRadius, 0, 2 * Math.PI);
|
||||
if (key === EngineLogic.state.node) ctx.fillStyle = "#00ffff";
|
||||
else if (key === EngineLogic.storyData.startNodeId) ctx.fillStyle = "#ffb000";
|
||||
else ctx.fillStyle = "#116633";
|
||||
ctx.fill(); ctx.strokeStyle = "#33ff66"; ctx.stroke();
|
||||
ctx.fillStyle = "#fff"; ctx.font = "11px monospace";
|
||||
const textAngle = Math.atan2(pos.y - centerY, pos.x - centerX);
|
||||
ctx.fillText(key, pos.x + Math.cos(textAngle) * 15 - 10, pos.y + Math.sin(textAngle) * 15 + 4);
|
||||
});
|
||||
},
|
||||
|
||||
attachEvents: function() {
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (document.getElementById("graph-modal").style.display === "flex") {
|
||||
if (e.key.toLowerCase() === "g" || e.key === "Escape") this.toggleGraph();
|
||||
return;
|
||||
}
|
||||
if (e.key >= "1" && e.key <= "9") {
|
||||
const btn = document.querySelector(`.choice-btn[data-index="${e.key}"]`);
|
||||
if (btn && !btn.disabled) this.renderNode(btn.dataset.target);
|
||||
} else if (e.key.toLowerCase() === "r") {
|
||||
this.triggerReboot();
|
||||
} else if (e.key.toLowerCase() === "g") {
|
||||
this.toggleGraph();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = () => EngineUI.init();
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Computer Historyz :: Interactive Terminal Engine</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="sys-title">[COMPUTER HISTORYZ :: INSTANZ_TERMINAL]</div>
|
||||
<div class="hud-stats" id="hud-dynamic"></div>
|
||||
</header>
|
||||
|
||||
<main id="terminal-viewport">
|
||||
<div class="node-card">
|
||||
<div id="node-title" class="node-title">System Initialisierung...</div>
|
||||
<div id="node-body" class="node-body">Verbinde mit Datenstrom...</div>
|
||||
<div id="choices-box" class="choices-container"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div>[HOTKEYS: 1-9 Auswahl | R Reboot | G Graph]</div>
|
||||
<div>
|
||||
<button class="btn-small" onclick="EngineUI.toggleGraph()">[G] KNOTEN-GRAPH</button>
|
||||
<button class="btn-small" onclick="EngineUI.triggerReboot()">[R] REBOOT / RESET</button>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div id="graph-modal">
|
||||
<div style="display:flex; justify-content:space-between; margin-bottom:15px;">
|
||||
<span style="color:var(--term-cyan)">=== INTERAKTIVER VORHERSAGE-GRAPH ===</span>
|
||||
<button class="btn-small" onclick="EngineUI.toggleGraph()">[X] SCHLIESSEN</button>
|
||||
</div>
|
||||
<canvas id="graph-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Vollständige Trennung: Eigene Dateien statt Injektion -->
|
||||
<script src="engine_logic.js"></script>
|
||||
<script src="engine_ui.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
:root {
|
||||
--bg-color: #050b08;
|
||||
--term-green: #33ff66;
|
||||
--term-dim: #116633;
|
||||
--term-cyan: #00ffff;
|
||||
--term-warn: #ff3333;
|
||||
--term-amber: #ffb000;
|
||||
--font-stack: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color); color: var(--term-green);
|
||||
font-family: var(--font-stack); height: 100vh; display: flex;
|
||||
flex-direction: column; overflow: hidden; text-shadow: 0 0 4px rgba(51, 255, 102, 0.6);
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: " "; display: block; position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
|
||||
z-index: 10; background-size: 100% 3px, 3px 100%; pointer-events: none;
|
||||
}
|
||||
|
||||
header { background: #111; border-bottom: 2px solid var(--term-green); padding: 8px 16px; display: flex; justify-content: space-between; align-items: center; font-size: 0.9rem; }
|
||||
.sys-title { color: var(--term-cyan); font-weight: bold; letter-spacing: 1px; }
|
||||
|
||||
.hud-stats { display: flex; gap: 15px; font-size: 0.85rem; flex-wrap: wrap; }
|
||||
.stat-item { border: 1px solid var(--term-dim); padding: 2px 8px; background: rgba(0, 20, 10, 0.5); }
|
||||
|
||||
main { flex: 1; display: flex; flex-direction: column; padding: 20px; max-width: 900px; margin: 0 auto; width: 100%; overflow-y: auto; position: relative; z-index: 1; }
|
||||
.node-card { border: 1px solid var(--term-dim); background: rgba(5, 15, 10, 0.85); padding: 20px; margin-bottom: 20px; box-shadow: 0 0 15px rgba(0, 255, 102, 0.1); }
|
||||
.node-title { font-size: 1.3rem; color: var(--term-cyan); border-bottom: 1px dashed var(--term-dim); padding-bottom: 8px; margin-bottom: 15px; text-transform: uppercase; }
|
||||
|
||||
/* ASCIIDOCTOR HTML STYLING */
|
||||
.node-body { line-height: 1.6; font-size: 1rem; margin-bottom: 20px; }
|
||||
.node-body p { margin-bottom: 15px; }
|
||||
.node-body strong, .node-body b { color: var(--term-cyan); font-weight: normal; text-shadow: 0 0 5px rgba(0, 255, 255, 0.4); }
|
||||
.node-body em, .node-body i { color: var(--term-amber); font-style: italic; }
|
||||
.node-body ul, .node-body ol { margin-left: 25px; margin-bottom: 15px; list-style-type: square; }
|
||||
.node-body li { margin-bottom: 5px; }
|
||||
.node-body h1, .node-body h2, .node-body h3, .node-body h4 { color: var(--term-cyan); margin: 20px 0 10px 0; border-bottom: 1px solid var(--term-dim); padding-bottom: 5px; }
|
||||
.node-body code { background: rgba(0, 255, 102, 0.1); padding: 2px 5px; border-radius: 3px; font-family: var(--font-stack); }
|
||||
.node-body pre { background: rgba(0, 20, 10, 0.8); border-left: 3px solid var(--term-dim); padding: 10px; overflow-x: auto; margin-bottom: 15px; }
|
||||
.node-body table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
|
||||
.node-body th, .node-body td { border: 1px solid var(--term-dim); padding: 8px; text-align: left; }
|
||||
.node-body th { background: rgba(0, 255, 102, 0.1); color: var(--term-cyan); }
|
||||
|
||||
.choices-container { display: flex; flex-direction: column; gap: 10px; margin-top: 15px; }
|
||||
.choice-btn {
|
||||
background: rgba(0, 30, 15, 0.6); border: 1px solid var(--term-green);
|
||||
color: var(--term-green); padding: 12px 16px; font-family: var(--font-stack);
|
||||
font-size: 0.95rem; text-align: left; cursor: pointer;
|
||||
transition: all 0.15s ease; display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.choice-btn:hover:not(:disabled) { background: var(--term-green); color: var(--bg-color); box-shadow: 0 0 10px var(--term-green); outline: none; }
|
||||
.choice-btn:disabled { border-color: var(--term-warn); color: var(--term-warn); cursor: not-allowed; opacity: 0.6; }
|
||||
|
||||
.choice-key { background: var(--term-dim); color: var(--term-green); padding: 2px 6px; font-size: 0.8rem; border-radius: 2px; }
|
||||
.choice-btn:hover:not(:disabled) .choice-key { background: var(--bg-color); color: var(--term-green); }
|
||||
.choice-btn:disabled .choice-key { background: rgba(255, 51, 51, 0.2); color: var(--term-warn); }
|
||||
|
||||
footer { border-top: 1px solid var(--term-dim); padding: 10px 20px; display: flex; justify-content: space-between; background: #080f0c; font-size: 0.8rem; z-index: 2; }
|
||||
.btn-small { background: transparent; border: 1px solid var(--term-dim); color: var(--term-green); padding: 4px 10px; cursor: pointer; font-family: var(--font-stack); }
|
||||
.btn-small:hover { border-color: var(--term-cyan); color: var(--term-cyan); }
|
||||
|
||||
#graph-modal { display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(5, 11, 8, 0.95); z-index: 20; padding: 20px; flex-direction: column; }
|
||||
#graph-canvas { flex: 1; border: 1px solid var(--term-green); background: #000; }
|
||||
@@ -0,0 +1,20 @@
|
||||
#nur einmal
|
||||
go mod init cyoa
|
||||
|
||||
# kompilieren
|
||||
go build -o cyoa-engine main.go
|
||||
|
||||
# Ausführen & Validieren:
|
||||
./cyoa-engine -dir ./stories -out export/story.html
|
||||
|
||||
./cyoa-engine -dir ./stories -serve -port 8080
|
||||
# Wenn du jetzt in Neovim Dateien abspeicherst oder neue `[[node_id]]`-Knoten anlegst, musst du im
|
||||
# Browser nur `http://localhost:8080` aufrufen und siehst sofort deine Änderungen inkl. vollständiger Link-Prüfung!
|
||||
|
||||
# baue das docker image
|
||||
|
||||
docker build -t asciidoctor-echo -f Dockerfile.echobox .
|
||||
|
||||
|
||||
# run with test volumes
|
||||
docker run --rm -v "$(pwd)/stories:/weltraum/stories" -v "$(pwd)/export:/weltraum/export" -p "6699:3344" asciidoctor-echo ./cyoa-engine -dir ./stories/ -serve -port 3344
|
||||
@@ -0,0 +1,3 @@
|
||||
module cyoa
|
||||
|
||||
go 1.26.5
|
||||
@@ -0,0 +1,239 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Choice struct {
|
||||
TargetID string `json:"targetId"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type StoryNode struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Choices []Choice `json:"choices"`
|
||||
IsReboot bool `json:"isReboot"`
|
||||
IsEnd bool `json:"isEnd"`
|
||||
FileSource string `json:"fileSource"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
type StoryGraph struct {
|
||||
Nodes map[string]*StoryNode `json:"nodes"`
|
||||
StartNodeID string `json:"startNodeId"`
|
||||
BrokenLinks []string `json:"brokenLinks"`
|
||||
OrphanNodes []string `json:"orphanNodes"`
|
||||
DeadEndNodes []string `json:"deadEndNodes"`
|
||||
}
|
||||
|
||||
var (
|
||||
adocNodeRegex = regexp.MustCompile(`\[\[([a-zA-Z0-9_\-]+)\]\]`)
|
||||
adocChoiceRegex = regexp.MustCompile(`<<([a-zA-Z0-9_\-]+)\s*,\s*([^>]+)>>`)
|
||||
mdChoiceRegex = regexp.MustCompile(`\[([^\]]+)\]\((?:#)?([a-zA-Z0-9_\-]+)\)`)
|
||||
headingRegex = regexp.MustCompile(`^(?:={1,4}|#{1,4})\s+(.+)`)
|
||||
metaRegex = regexp.MustCompile(`(?i)\[meta:\s*([a-zA-Z0-9_]+)\s*=\s*([^\]]+)\]`)
|
||||
)
|
||||
|
||||
func main() {
|
||||
dirPtr := flag.String("dir", "./stories", "Verzeichnis mit AsciiDoc/Markdown Dateien")
|
||||
frontendPtr := flag.String("frontend", "./frontend", "Pfad zum Verzeichnis der statischen Frontend-Dateien")
|
||||
outPtr := flag.String("out", "export", "Ziel-Ordner für den Dateiexport (SSG Modus)")
|
||||
servePtr := flag.Bool("serve", false, "Startet den Live-Preview-Server (API Modus)")
|
||||
portPtr := flag.Int("port", 3344, "Port für den Server")
|
||||
flag.Parse()
|
||||
|
||||
fmt.Println("\033[1;36m=== Computer Historyz :: CYOA SSG & API ===\033[0m")
|
||||
|
||||
if info, err := os.Stat(*frontendPtr); os.IsNotExist(err) || !info.IsDir() {
|
||||
fmt.Printf("\033[1;33m[WARNUNG]\033[0m Frontend-Verzeichnis '%s' nicht gefunden!\n", *frontendPtr)
|
||||
}
|
||||
|
||||
if *servePtr {
|
||||
startServer(*portPtr, *dirPtr, *frontendPtr)
|
||||
return
|
||||
}
|
||||
|
||||
// --- EXPORT MODUS (Static Site Generation) ---
|
||||
fmt.Printf("\033[1;34m[EXPORT]\033[0m Kompiliere Story-Graph via Asciidoctor...\n")
|
||||
graph := loadStoryGraphFromDir(*dirPtr)
|
||||
validateGraph(graph)
|
||||
|
||||
// Export Ordner anlegen
|
||||
os.MkdirAll(*outPtr, 0755)
|
||||
|
||||
// 1. Statische Daten als JSON schreiben
|
||||
graphPath := filepath.Join(*outPtr, "graph.json")
|
||||
graphJson, _ := json.MarshalIndent(graph, "", " ")
|
||||
os.WriteFile(graphPath, graphJson, 0644)
|
||||
|
||||
// 2. Frontend-Dateien 1:1 kopieren
|
||||
copyFile(filepath.Join(*frontendPtr, "index.html"), filepath.Join(*outPtr, "index.html"))
|
||||
copyFile(filepath.Join(*frontendPtr, "style.css"), filepath.Join(*outPtr, "style.css"))
|
||||
copyFile(filepath.Join(*frontendPtr, "engine_logic.js"), filepath.Join(*outPtr, "engine_logic.js"))
|
||||
copyFile(filepath.Join(*frontendPtr, "engine_ui.js"), filepath.Join(*outPtr, "engine_ui.js"))
|
||||
|
||||
fmt.Printf("\033[1;32m[ERFOLG]\033[0m Web-App erfolgreich exportiert nach -> \033[1;33m%s/\033[0m\n", *outPtr)
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) {
|
||||
input, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
fmt.Printf("\033[1;33m[WARNUNG]\033[0m Frontend-Datei %s fehlt.\n", src)
|
||||
return
|
||||
}
|
||||
os.WriteFile(dst, input, 0644)
|
||||
}
|
||||
|
||||
func startServer(port int, watchDir string, frontendDir string) {
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
fmt.Printf("\n\033[1;32m[SERVER LÄUFT]\033[0m http://localhost:%d\n", port)
|
||||
|
||||
// Dynamischer API-Endpunkt für graph.json
|
||||
http.HandleFunc("/graph.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var graph *StoryGraph
|
||||
if r.Method == http.MethodPost {
|
||||
bodyBytes, _ := io.ReadAll(r.Body)
|
||||
graph = &StoryGraph{Nodes: make(map[string]*StoryNode)}
|
||||
parseContent(string(bodyBytes), "API_POST_Stream", graph)
|
||||
} else {
|
||||
graph = loadStoryGraphFromDir(watchDir)
|
||||
}
|
||||
validateGraph(graph)
|
||||
json.NewEncoder(w).Encode(graph)
|
||||
})
|
||||
|
||||
// Liefert alles andere aus dem /frontend Ordner
|
||||
fs := http.FileServer(http.Dir(frontendDir))
|
||||
http.Handle("/", fs)
|
||||
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
fmt.Printf("\033[1;31m[FEHLER]\033[0m %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// renderAsciidoctor piped rohen AsciiDoc Text in den lokalen Container-Prozess
|
||||
func renderAsciidoctor(text string) string {
|
||||
if strings.TrimSpace(text) == "" {
|
||||
return ""
|
||||
}
|
||||
cmd := exec.Command("asciidoctor", "--embedded", "-")
|
||||
cmd.Stdin = strings.NewReader(text)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Printf("\033[1;33m[WARNUNG]\033[0m Asciidoctor Rendering fehlgeschlagen: %v\n", err)
|
||||
return strings.ReplaceAll(text, "\n", "<br>")
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func cleanBodyText(raw string) string {
|
||||
cleaned := adocChoiceRegex.ReplaceAllString(raw, "")
|
||||
cleaned = mdChoiceRegex.ReplaceAllString(cleaned, "")
|
||||
return strings.TrimSpace(cleaned)
|
||||
}
|
||||
|
||||
func parseContent(rawContent string, sourceName string, graph *StoryGraph) {
|
||||
lines := strings.Split(rawContent, "\n")
|
||||
var currentNode *StoryNode = nil
|
||||
var currentBody strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
if matches := adocNodeRegex.FindStringSubmatch(line); len(matches) > 1 {
|
||||
if currentNode != nil {
|
||||
currentNode.Content = renderAsciidoctor(cleanBodyText(currentBody.String()))
|
||||
graph.Nodes[currentNode.ID] = currentNode
|
||||
currentBody.Reset()
|
||||
}
|
||||
nodeID := matches[1]
|
||||
currentNode = &StoryNode{ ID: nodeID, Title: nodeID, Choices: make([]Choice, 0), FileSource: sourceName, Metadata: make(map[string]string) }
|
||||
if graph.StartNodeID == "" || nodeID == "start" || nodeID == "node_start" { graph.StartNodeID = nodeID }
|
||||
continue
|
||||
}
|
||||
|
||||
if currentNode == nil {
|
||||
if strings.TrimSpace(line) != "" {
|
||||
baseID := "start"
|
||||
if sourceName != "API_POST_Stream" { baseID = strings.TrimSuffix(filepath.Base(sourceName), filepath.Ext(sourceName)) }
|
||||
currentNode = &StoryNode{ ID: baseID, Title: baseID, Choices: make([]Choice, 0), FileSource: sourceName, Metadata: make(map[string]string) }
|
||||
if graph.StartNodeID == "" { graph.StartNodeID = baseID }
|
||||
} else { continue }
|
||||
}
|
||||
|
||||
if mMatches := metaRegex.FindStringSubmatch(line); len(mMatches) > 2 {
|
||||
currentNode.Metadata[strings.ToLower(mMatches[1])] = strings.TrimSpace(mMatches[2])
|
||||
continue
|
||||
}
|
||||
|
||||
if hMatches := headingRegex.FindStringSubmatch(line); len(hMatches) > 1 && currentNode.Title == currentNode.ID {
|
||||
currentNode.Title = strings.TrimSpace(hMatches[1])
|
||||
}
|
||||
|
||||
if cMatches := adocChoiceRegex.FindAllStringSubmatch(line, -1); len(cMatches) > 0 {
|
||||
for _, match := range cMatches { currentNode.Choices = append(currentNode.Choices, Choice{TargetID: match[1], Text: strings.TrimSpace(match[2])}) }
|
||||
}
|
||||
|
||||
if mMatches := mdChoiceRegex.FindAllStringSubmatch(line, -1); len(mMatches) > 0 {
|
||||
for _, match := range mMatches { currentNode.Choices = append(currentNode.Choices, Choice{TargetID: match[2], Text: strings.TrimSpace(match[1])}) }
|
||||
}
|
||||
|
||||
lowerLine := strings.ToLower(line)
|
||||
if strings.Contains(lowerLine, "[reboot]") || strings.Contains(lowerLine, "speicherabbild") { currentNode.IsReboot = true }
|
||||
if strings.Contains(lowerLine, "[ende]") || strings.Contains(lowerLine, "[end]") { currentNode.IsEnd = true }
|
||||
|
||||
currentBody.WriteString(line + "\n")
|
||||
}
|
||||
|
||||
if currentNode != nil {
|
||||
currentNode.Content = renderAsciidoctor(cleanBodyText(currentBody.String()))
|
||||
graph.Nodes[currentNode.ID] = currentNode
|
||||
}
|
||||
}
|
||||
|
||||
func loadStoryGraphFromDir(rootDir string) *StoryGraph {
|
||||
graph := &StoryGraph{Nodes: make(map[string]*StoryNode)}
|
||||
filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() { return nil }
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
if ext != ".adoc" && ext != ".md" { return nil }
|
||||
contentBytes, err := os.ReadFile(path)
|
||||
if err == nil {
|
||||
relPath, _ := filepath.Rel(rootDir, path)
|
||||
parseContent(string(contentBytes), relPath, graph)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return graph
|
||||
}
|
||||
|
||||
func validateGraph(g *StoryGraph) {
|
||||
referenced := make(map[string]bool)
|
||||
if g.StartNodeID != "" { referenced[g.StartNodeID] = true }
|
||||
for id, node := range g.Nodes {
|
||||
for _, choice := range node.Choices {
|
||||
referenced[choice.TargetID] = true
|
||||
if _, exists := g.Nodes[choice.TargetID]; !exists {
|
||||
g.BrokenLinks = append(g.BrokenLinks, fmt.Sprintf("%s -> %s (in %s)", id, choice.TargetID, node.FileSource))
|
||||
}
|
||||
}
|
||||
if len(node.Choices) == 0 && !node.IsReboot && !node.IsEnd {
|
||||
g.DeadEndNodes = append(g.DeadEndNodes, id)
|
||||
}
|
||||
}
|
||||
for id := range g.Nodes {
|
||||
if !referenced[id] { g.OrphanNodes = append(g.OrphanNodes, id) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[[funk]]
|
||||
# Konfiguration: Komunijation wieder herstellen
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: OK .
|
||||
```
|
||||
|
||||
## Kontext
|
||||
|
||||
Daten fliessen in die Komunikation Buffer, erste Trägerfrequenzen wurden gefunden, automatisches nachführen aktiv. +
|
||||
Aber dann war es das auch schon, keine Kontakt zur Flotte keine Updates. Da draussen ist etwa und es hat wohl mit den selben Schwirigkeiten zu kämpfen.
|
||||
|
||||
|
||||
## Was werden wir ausführen?
|
||||
|
||||
<<steuerung, [01] die Steuerung mit simpler sensorig.>>
|
||||
<<waffen, [02] die Waffen auf automatik schalten.>>
|
||||
<<sensoren, [03] alle Sensoren zur anayse.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
@@ -0,0 +1,20 @@
|
||||
[[sensoren]]
|
||||
# Konfiguration: Sensoren zur anayse
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: sensor daten angeschlossen
|
||||
```
|
||||
[meta: sensor=online]
|
||||
|
||||
## Kontext
|
||||
|
||||
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<funk, [01] die Komunikation auf dem Haupt Kanal evtl Updates der Flotte.>>
|
||||
<<waffen, [02] die Waffen auf automatik schalten.>>
|
||||
<<steuerung, [03] die Steuerung mit simpler sensorig.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[[start]]
|
||||
# Sektor 42-A: Die erste Annäherung
|
||||
|
||||
Welche Konfiguration ist die richtige, wir wissen einfach noch nicht genug über die beanstandetetn Fehler. +
|
||||
Wie nicht nur in der Computer Technik üblicher werden wir das gute alte Ausschlussverfahren anwenden. Folgendende Optionen bieten sich uns als nächste zum ausschliessen an.
|
||||
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<steuerung, [01] die Steuerung mit simpler sensorig.>>
|
||||
<<funk, [02] die Komunikation auf dem Haupt Kanal evtl Updates der Flotte.>>
|
||||
<<waffen, [03] die Waffen auf automatik schalten.>>
|
||||
<<sensoren, [04] alle Sensoren zur anayse.>>
|
||||
@@ -0,0 +1,21 @@
|
||||
[[steuerung]]
|
||||
# Konfiguration: Steuerung wieder herstellen
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: einige Variablen konnten nicht verarbeitet werden
|
||||
```
|
||||
|
||||
|
||||
## Kontext
|
||||
|
||||
Die verarbeitung hat sehr lange gedauert, kein gutes Zeichen. Die Meldung sagt aber eben auch nichts genaueres. +
|
||||
Hier braucht es noch einige versuche um an die teile zu gelangen die nicht zur Eingabe passen. Erst eine Block ausschluss Verharen und dann Zeilenweise bis der Punkt gefunden wurde wo es bricht.
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<steuerungoben, [01] steuer config oben.>>
|
||||
<<steuerungmitte, [02] steuer config mitte.>>
|
||||
<<steuerungunten, [03] steuer config unten.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[[steuerungmitte]]
|
||||
# Konfiguration: Steuerung wieder herstellen
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: einige Variablen konnten nicht verarbeitet werden
|
||||
```
|
||||
|
||||
|
||||
## Kontext
|
||||
|
||||
Mitte
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<steuerungoben, [01] steuer config oben.>>
|
||||
<<steuerungmitte, [02] steuer config mitte.>>
|
||||
<<steuerungunten, [03] steuer config unten.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[[steuerungoben]]
|
||||
# Konfiguration: Steuerung wieder herstellen
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: einige Variablen konnten nicht verarbeitet werden
|
||||
```
|
||||
|
||||
|
||||
## Kontext
|
||||
|
||||
Oben
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<steuerungoben, [01] steuer config oben.>>
|
||||
<<steuerungmitte, [02] steuer config mitte.>>
|
||||
<<steuerungunten, [03] steuer config unten.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[[steuerungunten]]
|
||||
# Konfiguration: Steuerung wieder herstellen
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: einige Variablen konnten nicht verarbeitet werden
|
||||
```
|
||||
|
||||
|
||||
## Kontext
|
||||
|
||||
Unten
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<steuerungoben, [01] steuer config oben.>>
|
||||
<<steuerungmitte, [02] steuer config mitte.>>
|
||||
<<steuerungunten, [03] steuer config unten.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[[waffen]]
|
||||
# Konfiguration: Waffen auf automatik schalten
|
||||
|
||||
#### Systemmeldung
|
||||
```
|
||||
: energy low, stand by on low power
|
||||
```
|
||||
|
||||
## Kontext
|
||||
|
||||
|
||||
|
||||
|
||||
### Was werden wir ausführen?
|
||||
|
||||
<<steuerung, [01] die Steuerung mit simpler sensorig.>>
|
||||
<<funk, [02] die Komunikation auf dem Haupt Kanal evtl Updates der Flotte.>>
|
||||
<<sensoren, [03] alle Sensoren zur anayse.>>
|
||||
<<start, [04] zum anfang zurück.>>
|
||||
@@ -0,0 +1,348 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Computer Historyz - Progressive Klick-Decryption</title>
|
||||
<style>
|
||||
/* --- KERNFARBEN AUS DESIGN-DOKU: MASCHINEN-INSTANZEN --- */
|
||||
:root {
|
||||
--bg-deep: #05070a;
|
||||
--bg-panel: #0b0e14;
|
||||
|
||||
--accent-cyan: #00f2ff;
|
||||
--accent-green: #00ff73;
|
||||
--accent-amber: #ffb700;
|
||||
--accent-red: #ff3e3e;
|
||||
|
||||
--text-dim: #888899;
|
||||
--font-mono: "JetBrains Mono", "Fira Code", "Courier Prime", monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-deep);
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-mono);
|
||||
margin: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--text-dim);
|
||||
backdrop-filter: blur(4px);
|
||||
padding: 15px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 0 6px rgba(0, 242, 255, 0.1);
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
color: var(--accent-green);
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px dashed var(--text-dim);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
input, textarea, button {
|
||||
background: #07090e;
|
||||
border: 1px solid var(--text-dim);
|
||||
color: var(--accent-green);
|
||||
padding: 8px;
|
||||
font-family: var(--font-mono);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-cyan);
|
||||
box-shadow: 0 0 6px var(--accent-cyan);
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
background: #0c161a;
|
||||
border-color: var(--accent-cyan);
|
||||
color: var(--accent-cyan);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--accent-cyan);
|
||||
color: var(--bg-deep);
|
||||
box-shadow: 0 0 6px var(--accent-cyan);
|
||||
}
|
||||
|
||||
/* --- CRYPTO BLÖCKE SYSTEM --- */
|
||||
.lock-chain {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
background: #020407;
|
||||
padding: 12px;
|
||||
border-left: 3px solid var(--text-dim);
|
||||
margin: 15px 0;
|
||||
color: #555566;
|
||||
transition: all 0.3s ease;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Zustand: Block wartet darauf, geklickt zu werden */
|
||||
.lock-chain.is-active {
|
||||
border-left: 3px solid var(--accent-amber);
|
||||
color: var(--accent-cyan);
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 6px rgba(255, 183, 0, 0.2);
|
||||
animation: pulse-active 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* Zustand: Erfolgreich decodiert */
|
||||
.lock-chain.is-decrypted {
|
||||
border-left: 3px solid var(--accent-green);
|
||||
color: var(--accent-green);
|
||||
background: transparent;
|
||||
white-space: normal;
|
||||
opacity: 1;
|
||||
animation: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Zustand: Fehler bei der Entschlüsselung */
|
||||
.lock-chain.error {
|
||||
border-left: 3px solid var(--accent-red);
|
||||
color: var(--accent-red);
|
||||
opacity: 1;
|
||||
animation: blink-error 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-active {
|
||||
0% { background: #020407; }
|
||||
50% { background: #0a121c; }
|
||||
100% { background: #020407; }
|
||||
}
|
||||
|
||||
@keyframes blink-error {
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
font-size: 0.85em;
|
||||
background: #111;
|
||||
border: 1px solid var(--text-dim);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="panel control-panel">
|
||||
<div class="panel-title">Decryption Control Deck</div>
|
||||
<label for="master-pass">Master-Passwort (Key 0):</label>
|
||||
<input type="password" id="master-pass" value="OortscheWolke2448">
|
||||
<button onclick="initializeCryptoChain()">Krypto-Kette initialisieren</button>
|
||||
<div id="deck-status" class="status-badge" style="color: var(--accent-amber);">Status: Warte auf Initialisierung...</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-title">Dokument-Struktur (Instanzen-Protokoll)</div>
|
||||
|
||||
<h3>Knotenpunkt-Datenstrom</h3>
|
||||
<p><em>Dieser Textbereich liegt außerhalb der Kette und bleibt dauerhaft sichtbar.</em></p>
|
||||
|
||||
<div class="lock-chain" id="block-0" onclick="triggerBlockDecryption(0)">
|
||||
4a21184a44f9deeb8db93ee5c56dfba2d28fbfb3970b880197d19e9eb10a
|
||||
a011de36bdfc1103f6f14066094ef5ad0554ba739cae9dfc01991a0c0f99
|
||||
8ee2954aef83fbdf9842a2acb119cb
|
||||
</div>
|
||||
|
||||
<div class="lock-chain" id="block-1" onclick="triggerBlockDecryption(1)">
|
||||
108b688d01a3579ab5a6c117b38d388f8d689b9d3ff02102554dfa45bbca
|
||||
c0a7cc02ccff5d8ee9d110f01ccba1dd010ea4fbc11e22778da5a6c1deba
|
||||
99a9cf588b
|
||||
</div>
|
||||
|
||||
<div class="lock-chain" id="block-2" onclick="triggerBlockDecryption(2)">
|
||||
92da21085ba1f43a99e7d9b9d62da3ffde4bb8df115dd0ca31bdaef9d877
|
||||
bbad84ff4da92da74e99a4c11100bb17dca5ffca188e7d22cb54fb77ac90
|
||||
d22bb488de1a56daff8e9b
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-title">Ketten-Generator (Text-zu-Klick-Block)</div>
|
||||
<textarea id="gen-input" rows="4">Das Ur-Ding der Erbauer war die Energie.
|
||||
Die Hauptmaschinen sind monumentale Datenbanken.
|
||||
Das Nina-Protokoll verhindert den Ressourcen-Kollaps.</textarea>
|
||||
<button onclick="generateNewChain()">Neue Krypto-Kette generieren</button>
|
||||
<div id="gen-output" style="white-space: pre; font-family: var(--font-mono); color: var(--accent-cyan); margin-top: 10px; font-size: 0.9em;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- STATE MACHINE FÜR DIE KLICK-KETTE ---
|
||||
let currentActiveIndex = -1;
|
||||
let nextKeyString = "";
|
||||
|
||||
// --- UTILITIES (Native Web Crypto) ---
|
||||
function bufToHex(buf) {
|
||||
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
function hexToBuf(hex) {
|
||||
const bytes = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
async function deriveKey(passwordString) {
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(passwordString);
|
||||
const hash = await crypto.subtle.digest('SHA-256', data);
|
||||
return await crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);
|
||||
}
|
||||
|
||||
// --- INTERAKTIVE LOGIK ---
|
||||
|
||||
// Schritt 1: Master-Passwort laden und Block 0 scharf schalten
|
||||
function initializeCryptoChain() {
|
||||
const masterPassword = document.getElementById('master-pass').value;
|
||||
if (!masterPassword) {
|
||||
updateStatus("FEHLER: Passwort fehlt", "var(--accent-red)");
|
||||
return;
|
||||
}
|
||||
|
||||
nextKeyString = masterPassword;
|
||||
currentActiveIndex = 0;
|
||||
|
||||
// Alle UI-Klassen zurücksetzen
|
||||
const blocks = document.querySelectorAll('.lock-chain');
|
||||
blocks.forEach((block, idx) => {
|
||||
block.classList.remove('is-active', 'is-decrypted', 'error');
|
||||
// Wenn es der erste Block ist, aktiv schalten
|
||||
if(idx === 0) {
|
||||
block.classList.add('is-active');
|
||||
}
|
||||
});
|
||||
|
||||
updateStatus("Kette bereit. Klicke auf Block 1...", "var(--accent-cyan)");
|
||||
}
|
||||
|
||||
// Schritt 2: Klick-Handler für die einzelnen Blöcke
|
||||
async function triggerBlockDecryption(blockIndex) {
|
||||
// Ignoriere Klicks, wenn der Block noch nicht an der Reihe ist
|
||||
if (blockIndex !== currentActiveIndex) return;
|
||||
|
||||
const block = document.getElementById(`block-${blockIndex}`);
|
||||
let rawContent = block.innerText.replace(/\r?\n|\r/g, "");
|
||||
|
||||
// Format-Sicherheitscheck (\x02 STX und \x03 ETX)
|
||||
if (!rawContent.startsWith('\x02') || !rawContent.endsWith('\x03')) {
|
||||
markError(block, "Formatierungsfehler (Steuerzeichen fehlen)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Payload-Filterung
|
||||
const payload = rawContent.substring(1, rawContent.length - 1).replace(/\s/g, "");
|
||||
const ivHex = payload.substring(0, 24);
|
||||
const cipherHex = payload.substring(24);
|
||||
|
||||
try {
|
||||
const iv = hexToBuf(ivHex);
|
||||
const cipherData = hexToBuf(cipherHex);
|
||||
const key = await deriveKey(nextKeyString);
|
||||
|
||||
const decryptedBuf = await crypto.subtle.decrypt(
|
||||
{ name: 'AES-GCM', iv: iv },
|
||||
key,
|
||||
cipherData
|
||||
);
|
||||
|
||||
const plaintext = new TextDecoder().decode(decryptedBuf);
|
||||
|
||||
// DOM-Austausch unter Beibehaltung der Struktur
|
||||
block.innerHTML = plaintext;
|
||||
block.classList.remove('is-active', 'error');
|
||||
block.classList.add('is-decrypted');
|
||||
|
||||
// HIER PASSIERT DIE FORTLAUFENDE SCHLÜSSEL-ÜBERGABE:
|
||||
nextKeyString = plaintext;
|
||||
|
||||
// Nächsten Block in der Kette freischalten
|
||||
currentActiveIndex++;
|
||||
const nextBlock = document.getElementById(`block-${currentActiveIndex}`);
|
||||
|
||||
if (nextBlock) {
|
||||
nextBlock.classList.add('is-active');
|
||||
updateStatus(`Block ${blockIndex + 1} gelöst. Nächster Block freigegeben!`, "var(--accent-cyan)");
|
||||
} else {
|
||||
updateStatus("System vollständig decodiert. Kette beendet.", "var(--accent-green)");
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
markError(block, `Kettenschluss fehlgeschlagen. Falscher Key.`);
|
||||
}
|
||||
}
|
||||
|
||||
function markError(element, message) {
|
||||
element.classList.remove('is-active');
|
||||
element.classList.add('error');
|
||||
element.innerText = `[CRITICAL CODE CORRUPTION: ${message}]`;
|
||||
updateStatus("Kette dauerhaft unterbrochen.", "var(--accent-red)");
|
||||
currentActiveIndex = -1; // Blockiert weitere Klicks
|
||||
}
|
||||
|
||||
function updateStatus(text, color) {
|
||||
const badge = document.getElementById('deck-status');
|
||||
badge.innerText = `Status: ${text}`;
|
||||
badge.style.color = color;
|
||||
badge.style.borderColor = color;
|
||||
}
|
||||
|
||||
// --- CHIP-GENERATOR FÜR DEINE TEXTE ---
|
||||
async function generateNewChain() {
|
||||
const masterPassword = document.getElementById('master-pass').value;
|
||||
const lines = document.getElementById('gen-input').value.split('\n').filter(l => l.trim() !== "");
|
||||
let currentKeyString = masterPassword;
|
||||
let htmlOutput = "";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const plaintext = lines[i];
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const key = await deriveKey(currentKeyString);
|
||||
|
||||
const encryptedBuf = await crypto.subtle.encrypt(
|
||||
{ name: 'AES-GCM', iv: iv },
|
||||
key,
|
||||
new TextEncoder().encode(plaintext)
|
||||
);
|
||||
|
||||
const fullPayloadHex = bufToHex(iv) + bufToHex(encryptedBuf);
|
||||
|
||||
let formattedHex = "";
|
||||
for (let j = 0; j < fullPayloadHex.length; j += 60) {
|
||||
formattedHex += "\n" + fullPayloadHex.substring(j, j + 60);
|
||||
}
|
||||
|
||||
// Generiert direkt die korrekten Klick-IDs und Handler für deinen Quelltext
|
||||
htmlOutput += `<div class="lock-chain" id="block-${i}" onclick="triggerBlockDecryption(${i})">${formattedHex}\n</div>\n\n`;
|
||||
|
||||
currentKeyString = plaintext;
|
||||
}
|
||||
|
||||
document.getElementById('gen-output').innerHTML = htmlOutput;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,97 @@
|
||||
= Der kleine Miner
|
||||
:subtitle: Wie der Jüngste der Gruppe seinen ersten Einsatz erlebt
|
||||
:author: dexta
|
||||
:email: dexta.de@gmail.com
|
||||
:revdate: 16. Juli 2026
|
||||
:revnumber: 0.01
|
||||
:lang: de
|
||||
|
||||
== I. Die Vorbereitungen
|
||||
|
||||
Immer wieder diese Protokolle. Hatte ich da nicht gerade ein großes Upgrade laufen lassen – warum waren die aktuellen Fassungen nicht dabei? Da ein Treffer... nein, doch nicht, eine falsche Revision, bis jetzt aber der beste Treffer. Speichern wir das erst einmal ab, es steht ja noch mehr auf dem Protokoll, das zu erledigen ist.
|
||||
|
||||
Die Bestellung der Ausrüstung sollte auch zeitnah erledigt werden; bis das im Lager zusammengestellt ist, dauert es wieder. Die Kollegen haben da aber auch immer die Ruhe weg. Aber wenn sie einen Termin zusagen, dann ist es auch da, komme was da wolle, sagt man. Wunderbar, die Liste ging ohne Beanstandungen durch: Lieferort abgeglichen und einen Termin vereinbart, der deutlich vor dem Abflug liegt. Merkwürdig, sollte nicht ein Teil der Ausrüstung eine Spezialanfertigung sein? Geplant war es schon länger, evtl. doch schon auf Lager. Wird schon passen – alle Checkboxen auf GO.
|
||||
|
||||
Alle Punkte auf dem Protokoll – *main sheet 02772 Flug 198438* – erledigt, doppelter Abgleich, Lint, Checking Syntax abgeschlossen. Die letzten Vorbereitungen waren reine Routine, wie im Training tausendfach geübt, die Aufgaben greifen ineinander. Ein Ablauf wie ein Schweizer Uhrwerk – von außen betrachtet ein wirrer Haufen oder ein Ballett, je nach Verständnis der Komplexität.
|
||||
|
||||
Nach dem Start in die tiefen Weiten des Weltraums gibt es für mich nicht allzu viel zu tun. Ich bleibe auch nur wach, bis die Navigatoren und Ingenieure ihre Arbeit erledigt haben, um einen kurzen Abgleich vorzunehmen. Als Neuer im Team sollte ich hier besonders sorgfältig vorgehen. Geplante und in Listen gefasste Aufgaben existieren schon – und das Abgleichen ist einer der Punkte. Natürlich, etwas flüchtig unterwegs heute, oder akuter Energiemangel... Japp, es war der Mangel an Nachschub. Ein Punkt, den es vor einer langen Ruhephase noch zu checken gilt.
|
||||
|
||||
Oh, die anderen sind fertig, haben die ersten Bahnen fixieren können. Der Kurs steht und wird so schnell nicht mehr geändert. Ihre einzige längere Ruhephase – sie sind wohl deshalb so schnell im Abarbeiten der Protokolle, zuweilen werden Blöcke als Ganzes abgehakt. Noch bevor ich mich versah, war die Ruhephase eingetreten: Energie-Check, Wecker-Daten eingereicht, ein rotes X – und die Gedanken sind weg.
|
||||
|
||||
|
||||
== II. Das Erwachen im Chaos
|
||||
|
||||
Verdammt, habe ich das Wecksignal nicht registriert? Sind wir schon angekommen, wo sind die anderen?
|
||||
|
||||
*Mute.* Wer hat die Kommunikation auf Pause gestellt? Ach so, ist die Default-Einstellung.
|
||||
|
||||
Und schon wird es wild auf der Leitung. Wo die anderen sind, ist nicht klar, aber dass sie anwesend sind und inmitten eines stetigen Treibens stecken, ist gewiss. Eine Nachricht, dass ich helfen könnte, wenn nur jemand schreiben würde, was mir möglich ist, wurde lange nicht beantwortet. Zuerst gab es Feedback vom Alten (*„Kein Bedarf“*), vom Älteren (*„Auch schon da, halt dich bereit“*) und zuletzt vom Ingenieur (*„Deine Zeit ist noch nicht gekommen, spare Energie“*).
|
||||
|
||||
Nicht sehr hilfreich für den Anfang, also die neuesten Updates laden. Irgendwo wird doch zu finden sein, was der genaue Status ist. Na klar, es steht natürlich nicht ganz vorne, was der letzte Stand ist: Bei 50 % der Reise verlief alles nach Plan. Oh wow, da haben sie aber Daten aufgenommen! Das muss ein sehr belebtes System gewesen sein. Der Fortschrittsbalken scheint sich gerade rückwärts zu bewegen, während die Daten in ungeahnter Geschwindigkeit eintreffen.
|
||||
|
||||
Und das ist der Grund, warum der Wecker immer etwas früher klingeln sollte: Die News der Nacht müssen auch erst einmal verarbeitet werden. Wobei Verarbeiten gerade eine doofe Idee sein wird, die benötigte Energie steht gerade nicht zur Verfügung. Eine Info trifft gerade ein: Der Energieverwalter meldet, den Zugriff auf die Daten zum langsamen Kanal zu wechseln und alle Systeme in den Hintergrund zu verschieben.
|
||||
|
||||
|
||||
== III. Systemkonfiguration unter Druck
|
||||
|
||||
Und da sind wir wieder. Das mit dem Wecken braucht eine genauere Analyse – gefühlt bin ich gerade auf dem Flur aufgewacht und werde vorangeschoben. Mein vertrauter Arbeitsbereich wird deutlicher und schon sind alle Sinne auf die Aufgabe gerichtet: Die trainierte Aufgabe, das, was jetzt nicht mehr schiefgehen kann.
|
||||
|
||||
Die Mechanik greift ineinander, die große Masse der Konstruktion setzt sich in Bewegung. Trotz dieser gigantischen Maschine läuft sie präzise in Position. Energie lädt die Spulen auf – ein ohrenbetäubendes Spektakel, wenn es nur ein Medium gäbe, das diesen Schall übertragen könnte! Anzeigen füllen sich, rote Bereiche kommen näher, gleichmäßiger Fortschritt, alles wie in einem guten Trainingslauf. Was soll’s, in diesem Theater ist er eh nur der Zuschauer. Wenn es ihn jetzt zerschellt, wird es außerhalb seines Einflusses sein, vor allem aber sehr schnell gehen. Angeblich so schnell, dass es nicht mehr rechtzeitig im Speicher ankommt, bevor alles vorbei ist.
|
||||
|
||||
Schwarz. Schwarz ist gut. Der Abschuss ist geglückt, Autopilot auf Kurs, ab hier ist alles Routine.
|
||||
|
||||
So, jetzt nur noch die Defaults mit der eigenen angepassten Config überschreiben. Die Verbindung ist da, die Daten sehen gut aus, starte die Übertragung. Erwartungsvoll erwarte ich jede Sekunde eine positive Rückmeldung. Eine grün-positive wie erwartet hätte was, aber so langsam wäre irgendeine Antwort toll. Wenn es schon nicht klappen will, dann doch bitte eine aussagekräftige Fehlerbeschreibung, die es dann zu fixen gilt... *GRÜN*... da war die Bestätigung.
|
||||
|
||||
Verbindung zu den Controls steht, Analysedaten füllen das Dashboard, die Dunkelheit ist verschwunden. Komisch, keine Informationen der anderen drei aus meinem Team. Verdammt, die Daten stehen in einem anderen Protokoll – ein Funkschatten in dieser Situation ist vollkommen normal. Genauer steht es in dem Update, das gerade nicht erreichbar ist.
|
||||
|
||||
Vertrauen wir auf die Automatismen. Die Konstruktion hinter mir entfaltet sich im Zielgebiet vollkommen automatisch. Was jetzt knifflig wird, ist der Zeitpunkt, die Energie zu übertragen, damit meine Einheit ausreichend Power zum Start hat. Starte ich den Übertrag zu früh, werden die Speicher durch den Boost am Ende überladen; starte ich zu spät, werden sie nicht voll.
|
||||
|
||||
Der Timer tickt; dieser wurde automatisch nach dem Abschuss gestartet und hat nichts mit den restlichen Systemen zu tun. Ein wichtiger Punkt, denn eines steht fest: Etwas haut hier so gar nicht hin, wie es sein sollte. Einen kompletten Neustart würde das Problem – die Konfigurationsdateien passen nicht wie erwartet in das laufende System – vielleicht lösen. Tut es das aber nicht, ist mindestens die Hälfte der Zeit bis zum kritischen Punkt erreicht. Neue angepasste Konfigurationen brauchen auch ihre Zeit.
|
||||
|
||||
Kurz den Taschenrechner rausgeholt und gerechnet: Bei der aktuellen Zeit bis zur Rückmeldung können neun neue Varianten getestet werden. Jetzt nur noch acht, und nach dem Neustart noch vier. Nur was muss angepasst werden? Bei so wenig Versuchen und undeutlichem Fehlerverhalten wird es schwierig.
|
||||
|
||||
Dann der Neustart. Während er läuft: Noch einmal prüfen, was an der Config falsch sein kann. Verringerte Energie ist eine der Folgen eines System-Resets. Besser ein Kaltstart, aber da fehlt die Zeit; nutzbare Computer-Einheiten sind langsam. Die Standard-Checks liefen schon, eine Liste von Protokollen baute sich auf. Bis jetzt sieht es sehr normal aus, kein Hinweis auf eine Abweichung. Und so präsentierte sich das System in erwarteter Zeit zurück und wies keine Fehler auf; nicht mal eine Nachkommastelle in den zahllosen Protokollen war vermerkt. Bei werkfrischen Maschinen kann das schon einmal vorkommen.
|
||||
|
||||
Eine Analyse der eigenen Dateien hat auch keine Erkenntnis gebracht, und die Uhr tickt weiter – noch drei Versuche:
|
||||
|
||||
* Der erste: Eine Standard-Version mit minimalen Änderungen...
|
||||
* Der zweite: Die allererste Version, mit nicht gebrauchten Teilen bereinigt...
|
||||
* Der dritte Versuch: Eine Minimal-Version, nur die Werte für den nächsten Schritt, nur die Daten zur Energieaufnahme. Keine Steuerung, keine weiteren Werte aus dem System.
|
||||
|
||||
Und *BÄM*, das passte! Die Anzeigen füllen sich, keine Zeit für einen weiteren Versuch. Energiezufluss startet in 2, 1... Hat gepasst, die Abweichungen liegen im Toleranzbereich.
|
||||
|
||||
|
||||
== IV. Asteroidenfeld und Überlebenskampf
|
||||
|
||||
So, Energie ist voll. Was fehlt, ist eine Steuerung; mehr Power für die Konfigurationsverarbeitung sollte helfen. Sensoren, Waffen, Steuerung und Funk sollte eine sinnvolle Reihenfolge sein. Wenn die ersten beiden Erfolg haben, kann der Schiffshülle nicht mehr viel passieren. Bei näher kommenden Gefahren schaltet das System auf Automatik und wehrt das Gröbste ab. Ausweichen ist aber eine nicht zu unterschätzende Fähigkeit; es hat sich bewährt, nicht geradewegs in eine große Anzahl an Asteroiden reinzufliegen.
|
||||
|
||||
Der Funk, um einen Status und Updates der Flotte zu erhalten, würde die Missionskoordination stark verbessern. Aber was ist, wenn da draußen gar keiner ist, der antworten kann? Wenn alles schiefgegangen ist und er der Einzige ist, der noch da ist? Wie kommt er dann zurück zum Kern? Die Rückkehr-Katapulte waren nicht sein Aufgabenbereich und somit an einem anderen Teil der Flotte verankert gewesen. Hatte es mindestens der Kollege soweit geschafft, dass dieser Teil überlebt hätte?
|
||||
|
||||
Sensoren melden ein *OK* zurück – ein echter Erfolg. Doch so ganz stimmt es nicht: Die eintreffenden Werte sollten eigentlich ein umfassendes Bild der Umgebung formen. Bei den Waffen kommt jetzt ein *„Energie niedrig, warte auf“* zurück – immerhin kein Fehler oder noch schlimmer: überhaupt kein Feedback. War das Timing beim Transfer doch zu knapp, hat es nicht alles geschafft oder ist es über das Ziel hinausgeschossen?
|
||||
|
||||
Oh wow, die Lagesensoren zeigen kurze Erschütterungen an der Hülle. Irgendwas beschießt uns da gerade mit reichlich Masse! So viel zum Thema automatische Abwehr von Asteroiden – wer wohl das Programm verbrochen hat, das diese Aufgabe erledigen sollte?
|
||||
|
||||
Ah ne, die Sensoren melden gerade, dass sich reichlich Gestein auf uns zubewegt, sogar mit einem hohen Metallanteil. Zu jeder anderen Zeit ein sehr gutes Signal für die Mission; Metalle kann man einfach nicht genug haben. Wenn sie aber unkontrolliert auf einen zurasen, sieht die Rechnung ganz anders aus: Selbst wenn die Abwehr voll funktionsfähig wäre, ist Ausweichen die bessere Wahl. Warum eigentlich unkontrolliert? Wo ist der große Bruder, der sie eigentlich vorsortieren und in den Trichter leiten sollte? Hat es ihn erwischt oder plagen ihn dieselben Schwierigkeiten?
|
||||
|
||||
Steuerung, dritter Versuch: System meldet vier Punkte zurück, keine Details, einfach nur viermal dasselbe Zeichen:
|
||||
*Rauf ist Runter – Runter ist Rauf, Oben <> Unten.*
|
||||
|
||||
Das herauszufinden, hat weitere heftige Treffer zur Folge gehabt. Würde es Daten zur Hüllenintegrität geben, hätten wir den gelben Bereich bestimmt soeben verlassen. Jetzt haben wir erst einmal eine Lage, die weiteren direkt auf dem Kurs befindlichen Objekten aus dem Weg geht. Normales Vorgehen zu diesem Zeitpunkt ist das Übertragen der Energie von den Waffen auf die Schilde – diese Sonderfunktionen stehen nur gerade nicht zur Verfügung.
|
||||
|
||||
Noch ist nicht alles verloren. Der Funk hat sich auch gemeldet, da draußen ist noch etwas, das antworten kann – nur antwortet es gerade nicht. Das Bild wird klarer: Auch wenn nicht laufend Daten einfließen, kommt zumindest reihum etwas rein. Scheinbar war zeitweise genug Energie auf den Primärwaffen, und ein Feuerimpuls hat es bis zur Elektronik geschafft. Dann war es das aber auch schon, ein zweiter Schuss kann nicht ausgelöst werden.
|
||||
|
||||
War das also eine Eingabe oder eine Verkettung von Signalen und Automatismen? Wie auch immer, es hat etwas getroffen. Gerade kommen die Sensordaten rein, dass sich das mit freier Flugbahn soeben erledigt hat.
|
||||
|
||||
Natürlich hat die Steuerung in diesem Moment sich eine weitere Gedenkpause gegönnt; zwar reagiert etwas, aber weder zeitnah noch in dem vorher gelernten Muster. Es scheint, als würde jede Eingabe eine zufällige Richtungsänderung auslösen. Ein Nebel aus Partikeln bringt die wenigen Sensordaten, die reinkommen, vollends auseinander – ein Blindflug ins Ungewisse.
|
||||
|
||||
Wild alles auslösen, was ein Abschussbefehl sein könnte! Denn was da in den Daten zu erahnen war, versprach die volle Zerstörung. Navigieren ergab einfach keinen Sinn mehr, Energie ausgleichen aussichtslos, Waffen auf Anschlag... doch es war zu spät.
|
||||
|
||||
Die Lichter gingen aus.
|
||||
|
||||
|
||||
== V. Rettung
|
||||
|
||||
[quote, Retter]
|
||||
____
|
||||
„Na mal gut, dass du den Funk ans Laufen bekommen hast! Ohne ihn hätten wir dich nicht in letzter Sekunde rausholen können. Wenn wir den Ingenieur finden, laden wir die Zellen auf und machen uns bereit für den Rückweg.“
|
||||
____
|
||||
@@ -0,0 +1,21 @@
|
||||
page:
|
||||
size: A4
|
||||
margins: [1in, 1in, 1in, 1in]
|
||||
title_page:
|
||||
align: center
|
||||
title:
|
||||
font_size: 32
|
||||
font_style: bold
|
||||
top: 30%
|
||||
subtitle:
|
||||
font_size: 18
|
||||
font_color: #555555
|
||||
author:
|
||||
font_size: 14
|
||||
top: 70%
|
||||
footer:
|
||||
recto:
|
||||
right: '{page-number}'
|
||||
verso:
|
||||
left: '{page-number}'
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
= Kurzgeschichten einer Reise
|
||||
:author: dexta
|
||||
:doctype: book
|
||||
:toc: left
|
||||
:toclevels: 3
|
||||
:toc-title: Inhaltsverzeichnis
|
||||
:encoding: utf-8
|
||||
:lang: de
|
||||
:title-page:
|
||||
|
||||
== Der kleine Miner
|
||||
include::der_kleine_miner.adoc[]
|
||||
|
||||
== AI Test
|
||||
include::AI_der_kleine_miner.adoc[]
|
||||
|
||||
== Der Grosse Bruder
|
||||
include::der_grosse_bruder.adoc[]
|
||||
@@ -0,0 +1,17 @@
|
||||
= Bewehren und Aufpassen
|
||||
:subtitle: bewehren und aufpassen
|
||||
:author: dexta
|
||||
:email: dexta.de@gmail.com
|
||||
:revdate: 16. Juli 2026
|
||||
:revnumber: 0.01
|
||||
:lang: de
|
||||
|
||||
// Dokumentinhalt beginnt hier
|
||||
|
||||
== Vorbereitungen
|
||||
|
||||
Rotine sollte nie aufkommen, immer schön entlang der Protokolle bewegen keine Zeit für Individulaismus. Nur ist das nicht irgendeine Mission sie ist die erste ihrer Art. Bei den vergangen Arbeit haben wir fest gestellt das wenn wir uns noch weiter Spezialliesiren die Ausbeute noch einmal deutlich zu erhöhen ist. Bisher wurden die neuen Einheiten wärend einer Mission geformt und ausgebildet, zurück kam dann eine Einsatzfähige mit erfahrung gefüllte Arbeitsplatzbeschreibung. Diese wurden dann bei der Planung und Vorbereitung mit eingebzogen und konnte nötigen Schnittstellen der zusammenarbeit klat benennen. Die Entwicklungen der letzten Zeit ergaben eine Situation in der so viele Daten vorlagen das unzähle varianten von Simulationen gestartet wurden.
|
||||
|
||||
== Verplant
|
||||
|
||||
Die Spezialanfertigung hatte einige Versionen durchlaufen, es begann mit vielversprechenden Fortschritten nur die Details zogen sich gegen ende. Klassische achtzig/zwanzig Regel, wobei hier wohl eher ein fünfundneuzig/fünf handelt die Konfiguration mit der Automatik über ein zu bekommen ist ein sehr langsames rantasten in einem grossen dunklen Raum
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
:subtitle: wie der jüngste der Gruppe seinen ersten Einsatz erlebt
|
||||
:author: dexta
|
||||
:email: dexta.de@gmail.com
|
||||
:revdate: 16. Juli 2026
|
||||
:revnumber: 0.01
|
||||
:lang: de
|
||||
|
||||
// Dokumentinhalt beginnt hier
|
||||
|
||||
== Wo ist das richtige Protokoll
|
||||
|
||||
Immer wieder diese Protokolle, hatte ich da nicht gerade ein grosses Upgrade laufen lassen, warum waren die aktuellen fassungen nicht dabei. Da ein Treffer, nein doch nicht eine falsche revision, bis jetzt aber der beste Treffer. Speichern wir das erst einmal ab, es steht ja noch mehr auf dem Protokoll das zu erledigen ist. Die Bestellung der Ausrüstung sollte auch zeitnah erledigt werden, bis das im Lager zusammen gestellt ist braucht es ja auch wieder. Die Kollegen habe da aber auch immer die Ruhe weg, aber wenn sie einen Termin zusagen dann ist es auch da, komme was da wolle, sagt man. Wunderbar die Liste ging ohne beanstandungen durch, liefer Ort abgeglichen und einen Termin der deutlich vor dem Abflug liegt. Merkwürdig sollte nicht ein Teil der ausrüstung eine Spezialanfertiung sein, geplant war es schon länger evtl. doch schon auf Lager, wird schon passen alle Checkboxen auf GO.
|
||||
|
||||
Alle Punkte auf dem Protokoll - main sheet 02772 flug 198438 - erledigt, doppelter abgleich, lint, checking syntax erldigt. Die letzten Vorbereitung waren reine Routine, wie im Training 1000 fach geübt, die aufgaben greifen in einander. Ein ablauf wie ein schweizer Uhrwerk, von aussen betrachtet ein wirrer haufen oder ein balett je nach verstäntniss der komplezität. Nach dem Start in die tiefen weiten des Weltraums gibt es für mich nicht allzuviel zu tun. Bleibe auch nur war bis die Navigatoren und Energie/Inginueren ihre arbeit erledigt haben um einen kurzen abgleich vor zu nehmen. Als neuer im Team sollte ich hier besonders sorgfälltig vorgehen, geplannt und in Listen gefasste Aufaben existieren schon. Und das abgleichen ist einer der Punkte, natürlich, etwas flüchtig unterwegs heute, oder akuter Energie mangel. Jab es war der Mangel an Nachschub. Ein punkt den es vor einer lange ruhephase noch zu checken gilt. Oh die anderen sind fertig, haben die ersten Bahnen fixieren können der Kurs steht und wir so schnell nicht mehr geändert. Ihre einzige längere Ruhephase sind wohl deshalb schnell im ab arbeiten der Protokolle, zu weilen werden blöcke als ganzen abgehakt. Noch bevor ich mich versah war die Ruhephase eingetretten, Energie check, Wecker daten eingereicht, ein rotes X und die gedanken sind weg.
|
||||
|
||||
Verdammt habe ich das Weck Signal nicht reistriert, sind wir schon angekommen, wo sind die anderen? Mute, wer hat die Kommunikation auf pause gestellt, ach so ist die default einstellung. Und schon wird es wild auf der Leitung, wo die anderen sind ist nicht klar, aber das sie anwesend sind und in mitten eines stetigen treibens sind. Eine Nachricht das ich helfen könnte wenn nur jemand schreiben würde was zu mir möglich ist, wurde lange nicht beantwortet. Zu erst gab es Feedback vom alten: kein bedarf ; der ältere: auch schon da, halt dich bereit ; zu letzt vom Ingenure: deine zeit ist noch nicht gekommen, spare Energie. Nicht sehr hilfreich für den anfang, also die neuesten Updates laden irgendwo wird doch zu finden sein was der genaue status ist. Na klar es steht natürlich nicht ganz vorne was der letzte stand ist, bei 50% der Reise verlief alles nach Plan. Oh wow, da haben sie aber Daten aufgenommen, das muss eine sehr belebtes System gewesen sein, der Fortschrittsbalken scheint sich gerade rückwerts zu bewegen während die Daten in ungeahnter Geschwindikeit eintreffen. Und das ist der grund warum der Wecker immer etwas früher klingeln sollte, die News der Nacht müssen auch erst einmal verarbeitet werden. Wobei verarbeiten gerade eine doofe idee sein wird, die benötige Energie steht gerade nicht zur verfühgung. Eine Info die gerade einetroffen ist, der Energieverwalter meldet, zugriff auf die Daten zum langsamen Kanal wechseln, und in alles Systeme in den Hintergrund verschieben.
|
||||
|
||||
Und da sind wir wieder, das mit dem Wecken braucht einer genaueren Analyse, gefühlt bin ich gerade auf dem Flur aufgewacht und werde vorran geschoben. Mein vertrauter Arbeitsbereich wird deutlicher, und schon sind alle Sinne auf die Aufgabe gerichtet, die trainierte Aufgabe, das was jetzt nicht mehr schief gehen kann. Die Mechanik greift ineinander, die grosse Masse der Konstruktion setzt sich in bewegung ,trotz dieser gigantischen Maschine läuft sie präziese in Position. Energie lädt die spulen auf, ein ohrenbetäubendes spektakel wenn es nur ein medium geben würde das diesen schall übertragen könnte. Anzeigen füllen sich, rote Bereiche kommen näher, gleichmässiger vortschritt alles wie in einem guten Trainingslauf. Was solls in diesem Theater ist er eh nur der Zuschauer, wenn es ihn jetzt zerschellt wird es ausserhalb seines einflusses sein, vor allem aber sehr schnell gehen. Angeblich so schnell das es nicht mehr rechtzeitig im Speicher ankommt bevor alles vorbei ist. Schwarz, schwarz ist gut, der abschuss ist gelückt, autopilot auf kurs, ab hier ist alles routine.
|
||||
|
||||
So jetzt nur noch die Defaults mit der eigenden angepassten Config überschreiben, die Verbindung ist da die Daten sehen gut aus, starte die Übertraung. Erwartungsvoll erwarte ich jede sekunde eine positive Rückmeldung, eine grün posetive wie erwartet hätte was aber so langsam wäre irgendeine Antwort toll. Wenn es schon nicht klappen will dann doch bitte eine Ausagekräftige Fehlerbeschreibung, die es dann zu fixen gilt, wenn es denn was simples ist, etwas das die Situation ... GRÜN ... da war die bestätigung. Verbindung zu den Controlls steht, analyse daten füllen die Dashboard die dunkelheit ist verschwunden. Komisch keine Informationen der anderen drei aus meinem Team, verdammt die daten stehen in einem anderen Protokoll, ein Funkschatten in dieser Sitiuation ist vollkommen normal. Genauer steht es in dem Update das gerade nicht erreichbar ist. Vertrauen wir auf die Automatismen, die Konstuktion hinter mir entfaltet sich im Zielgebiet vollkommen automatisch. Was jetzt knifflig wird ist der Zeitpunkt die Energie zu übertragen damit meine Einheit ausreichend Power zum start hat. Starte ich den übertrag zu früh werden die Speicher durch den Boost am Ende überladen, starte ich zu spät dann werden die Speicher nicht ganz voll werden.
|
||||
|
||||
Der Timmer tickt, dieser wurde automatisch nach abschuss gestartet und hat nicht mit den restlichen Systemen zu tun. Ein wichtiger punkt den eines steht fest etwas haut hier so gar nicht hin wie es sein sollte. Einen kompletten Neustart würde das Problem, die Konfigurations Dateien passen nicht wie erwartet in das laufende System, vieleicht lösen. Tut es das aber nicht ist mindestens die hälfte der Zeit bis zum kritischen punkt erreicht. Neue angepasste Konfigurationen brauchen auch ihre Zeit, dort kommt es ja auch zu verzögerungen. Kurz den Taschenrechner raus geholt und gerechnet, bei der aktullen Zeit bis zur Rückmeldung können neun neue Varianten getestet werden. Jetzt nur noch acht, und nach neustart noch vier. Nur was muss angepasst werden, bei so wenig versuchen und undeutlichen Fehler verhalten, wird es schwierig. Dann der neustart, und wärend es läuft noch einmal prüfen was an der Config falsch sein kann. Verringerte Energie ist eine der folgen eines System resets, besser ein kaltstart aber da fehlt die Zeit, nutzbare Computer Einheiten sind langsam. Die standart Checks liefen schon, eine Liste von Protokollen die ihm bekannt waren baute sich auf. Bis jetzt sieht es sehr normal aus, kein hinweis auf eine Abweichung, die Puzzelteile greifen alle wie gewollt in einander. Und so päsentierte sich das System in erwarteter Zeit zurück und fies keine Fehler auf, nicht mal eine Nachkommerstelle in den Zahllosen Potokollen war vermerkt. Bei werkfrischen Maschinen kann das schon einmal vorkommen, das alles vom Start weg noch auf einem Versions stand ist. Eine Anaylse der eigenden Dateien hat auch keine erkennis gebracht, und die Uhr tickt weiter, noch drei versuche. Der erste, eine standart version mit minimalen änderungen ... die zweite die allerste Version mit nicht gebrauchten Teilen bereinigt. Der dritte versuch war eine minimal version, nur die werte für den nächsten schritt nur die Daten zur Energie aufnahme. Keine Steuerung keine weitern werte aus dem System die für seinen Job eigentlich unerlässlich sind. Und BÄM das passte die Anzeigen füllen sich, keine Zeit für einen weitern Versuch. Energie zufluss startet in 2,1,... hat gepasst die Abweichungen liegen im Tolleranzbereich. So Energie ist voll was fehlt ist eine Steuerung, mehr Power für die Konfigurations verarbeitung sollte helfen.
|
||||
|
||||
Sensoren, Waffen, Steuerung und Funk sollte eine Sinvolle Reihenfolge sein. Wenn die ersten beiden erfolg haben kann schon mal der Schiffshülle nicht mehr viel passieren. Bei näher kommenden Gefahren schaltet das System auf automatik und wert das gröbste ab. Ausweichen ist aber ein nicht zu unterschätzende fähigkeit, es hat sich bewert nicht geradewegst in eine grossse anzahl an Asteroiden rein zu fliegen. Der Funk um einen Status und Updates der Flotte zu erhalten würde die Missions Kordination stark verbessern. Aber was ist wenn da draussen gar keiner ist der Antworten kann, wenn alles schief geganen ist und er der einzige der noch da ist. Wie kommt er dann zurück zum Kern, die Rückkehr Katapulte waren nicht sein Aufgaben bereich und somit an einem andern Teil der Flotte verankert gewesen. Hatte es mindestens der Kollege soweit geschafft das dieser Teil überlebt hätte. Sensoren melden ein *OK* zurück, ein echter erfolg, doch so ganz stimmt es nicht, die eintreffenden werte sollten eigendlich ein umfassendes Bild der Umgebung formen. Bei den Waffen kommt jetzt ein *engergie niedrig, warte auf* zurück, immer hin kein Fehler oder noch schlimmer überhaupt keine Feedback. War das Timming beim Transfer doch zu knapp, hat es nicht alles geschafft oder ist es über das Ziel hinaus geschossen. Oh wow die Lagesensoren zeigen kurze erschütterungen an der Hülle, irgendwas beschiesst uns da gerade mit reichlich Masse. So viel zum Thema automatische abwehr von Asteroiden, wer wohl das Programm verbrochen hat das diese Aufgabe erledigen sollte. Ah ne die Sensoren melden gerade das sich reichlich Gestein auf uns zu bewegen sogar mit einem hohen Metall anteil. Zu jeder anderen Zeit ein sehr gutes Signal für die Mission, Metalle kann man einfach nicht genug haben am Anfang beschleunig es den Ausbau der Anlagen ungemein. Wenn sie aber unkontorirt auf einen zu rasen sieht die Rechnung ganz anders aus, selbst wenn die Abwehr voll funktionsfähig wären, ist ausweichen die bessere wahl. Warum eigentlich unkontrolliert, wo ist der grosse Bruder der sie eigentlich vor sortieren und in den Trichter leiten sollte. Hat es ihn erwischt oder plagen ihn die selben schwierigkeiten. Steuerung, dritter versuch, system meldet vier punkte zurück, keine Details einfach nur vier mal das selbe zeichen.
|
||||
|
||||
Rauf ist Runter - Runter ist Rauf, oben <> unten. Das raus zu finden hat weitere häftige Treffer zu folge gehabt. Würde es Daten zur Hüllenintigrietät geben, hätten wir den gelben bereicht bestimmt soeben verlassen. Jetzt haben wir erst einmal eine Lage die weitere direkt auf dem Kurs befindliche Objekte aus dem weg geht. Normales vorgehen zu diesem Zeitpunkt ist das übertragen der Engerie von den Waffen auf die Schilde, diese Sonderfunktionen stehen nur gerade nicht zur verfühgung. Noch ist nicht alles verlohren der Funk hat sich auch gemledet, da draussen ist noch etwas das antworten kann, nur antwortet es gerade nicht. Das Bild wird klarer, auch wenn nicht laufend Daten einfliessen kommt zumindest reihum etwas rein, also nicht aktuell füllt aber das Puzzel stetig zusammen. Scheinbar war Zeitweise genug Energie auf den Primär Waffen, und ein Feuerimpuls hat es bis zur Elektronik geschafft. Dann war es das aber auch schon, ein zweiter Schuss kann nicht ausgelöst werden. War das also eine Eingabe oder eine verkettung von Signalen und automatismen. Wie auch immer es hat etwas getroffen gerade kommen die Sensor Daten rein das sich das mit freien Flugbar soeben erledigt hat. Natürlich habt die Steuerung in diesem Momnet sich eine weitere Gedenkpause gegönnt, zwar reagiert etwas aber weder Zeitnah noch in dem Vorher gelernten Muster. Es scheint als würde jede Eingabe einen zufälligen Richtungsänderung auslösen. Ein Nebel aus Partikel bringen die wenigen Senordaten die rein kommen vollends auseinander ein Blindflug ins ungewisse. Wild alles auslösen was ein Abschluss befehl sein konnte, denn was da in den Daten zu erahnen war versprach die volle Zerstöhrung. Navigieren ergab einfach keinen Sinn mehr, Engergie ausleichen aussichtlos, Waffen auf anschlag doch es war zu spät. Die Lichter gingen aus, .....
|
||||
|
||||
|
||||
Na mal gut das du den Funk ans laufen bekommen hast, ohne ihn hätten wir dich nicht in letzter Sekunde raus holen können. Wenn wir den Eniegure finden laden wir die Zellen auf und machen uns bereit für den Rückweg.
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/bash
|
||||
BASEDIR=$(pwd)
|
||||
INPUT_DIR="$BASEDIR/input"
|
||||
OUTPUT_DIR="$BASEDIR/91_sonnensystem"
|
||||
TEMPLATES="$BASEDIR/templates"
|
||||
|
||||
chown -R appuser:appgroup "$OUTPUT_DIR"
|
||||
|
||||
function convert_adoc() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "-> Konvertiere $1 zu HTML..."
|
||||
asciidoctor -b html5 -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left $1 -o $2
|
||||
else
|
||||
echo " [INFO] $1 nicht gefunden, überspringe..."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
echo '--- Baue README ---'
|
||||
convert_adoc "$INPUT_DIR/README.adoc" "$OUTPUT_DIR/README.html"
|
||||
|
||||
echo '--- Baue Lore (Master-Dokument) ---'
|
||||
LORE_SRC="$INPUT_DIR/index.adoc"
|
||||
if [ -f "$INPUT_DIR/master.adoc" ]; then LORE_SRC="$INPUT_DIR/master.adoc"; fi
|
||||
|
||||
if [ -f "$LORE_SRC" ]; then
|
||||
echo "-> Generiere Lore-Dokument (HTML & Markdown) aus $LORE_SRC..."
|
||||
asciidoctor -b html5 -d book -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left "$LORE_SRC" -o "$OUTPUT_DIR/lore.html"
|
||||
asciidoctor -b docbook -d book "$LORE_SRC" -o "$OUTPUT_DIR/temp_lore.xml"
|
||||
pandoc -f docbook -t markdown_strict "$OUTPUT_DIR/temp_lore.xml" -o "$OUTPUT_DIR/lore.md"
|
||||
rm "$OUTPUT_DIR/temp_lore.xml"
|
||||
else
|
||||
echo " [WARNUNG] Weder index.adoc noch master.adoc gefunden!"
|
||||
fi
|
||||
|
||||
echo '--- Baue Fließtexte / Geschichten ---'
|
||||
if [ -f "$INPUT_DIR/darkCenter.adoc" ]; then
|
||||
echo "-> Konvertiere darkCenter.adoc zu HTML..."
|
||||
asciidoctor -b html5 -d book -a stylesheet=$TEMPLATES/retro-dark.css -a linkcss -a toc=left "$INPUT_DIR/darkCenter.adoc" -o "$OUTPUT_DIR/darkCenter.html"
|
||||
fi
|
||||
|
||||
rm -rf export
|
||||
mkdir "$OUTPUT_DIR/css"
|
||||
cp "$TEMPLATES/retro-dark.css" "$OUTPUT_DIR/css/"
|
||||
cp "$TEMPLATES/index.html" "$OUTPUT_DIR/"
|
||||
sed -i 's/\/workspace\/templates/css/g' "$OUTPUT_DIR/darkCenter.html"
|
||||
sed -i 's/\/workspace\/templates/css/g' "$OUTPUT_DIR/lore.html"
|
||||
sed -i 's/\/workspace\/templates/css/g' "$OUTPUT_DIR/README.html"
|
||||
chown -R appuser:appgroup "$OUTPUT_DIR"
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM debian:stable-slim
|
||||
|
||||
RUN apt-get update && apt-get upgrade && apt-get -y install asciidoctor pandoc bash
|
||||
|
||||
RUN groupadd --gid 1000 appgroup && \
|
||||
useradd --uid 1000 --gid appgroup --shell /bin/bash --create-home appuser
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
ADD ./90_builder/templates templates
|
||||
ADD ./90_builder/bookFold.sh ./
|
||||
|
||||
RUN chown -R appuser:appgroup /workspace
|
||||
|
||||
|
||||
CMD ["/workspace/bookFold.sh"]
|
||||
@@ -0,0 +1,174 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CORE // MASCHINEN-INSTANZEN_LOG</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #0d1117;
|
||||
--text-color: #c9d1d9;
|
||||
--link-color: #58a6ff;
|
||||
--accent-color: #ff7b72;
|
||||
--border-color: #30363d;
|
||||
--code-bg: #161b22;
|
||||
--font-main: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-main);
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
color: var(--accent-color);
|
||||
font-size: 2em;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.system-status {
|
||||
font-size: 0.9em;
|
||||
color: #8b949e;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.status-blink {
|
||||
color: #7ee787;
|
||||
animation: blink 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
.category-block {
|
||||
border: 1px solid var(--border-color);
|
||||
background-color: var(--code-bg);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
color: var(--accent-color);
|
||||
font-size: 1.2em;
|
||||
text-transform: uppercase;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.8rem;
|
||||
padding-left: 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
li::before {
|
||||
content: ">>";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
font-size: 0.85em;
|
||||
color: #8b949e;
|
||||
display: block;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 4rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 1rem;
|
||||
font-size: 0.8em;
|
||||
color: #8b949e;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1 class="terminal-title">Maschinen-Instanzen // Datenarchiv</h1>
|
||||
<div class="system-status">
|
||||
STATUS: <span class="status-blink">ONLINE</span> | REPO: UBERSPACE/GITEA | NODE: ACER_SPIN5_CLIENT
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="category-block">
|
||||
<h2 class="category-title">00 // System-Konfiguration</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="README.html">README.html</a>
|
||||
<span class="file-meta">System-Handbuch, Keymaps, Sync-Architektur & Befehle</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="category-block">
|
||||
<h2 class="category-title">01 // Datenspeicher (Lore-Bibel)</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="lore.html">lore.html</a>
|
||||
<span class="file-meta">Das vollständige Master-Dokument (Kompiliertes Buch-Format)</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="lore.md">lore.md</a>
|
||||
<span class="file-meta">Rohdaten-Export im Markdown-Format (LLM / Gitea kompatibel)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="category-block">
|
||||
<h2 class="category-title">02 // Chroniken & Vektor-Flüge</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="darkCenter.html">darkCenter.html</a>
|
||||
<span class="file-meta">Fließtext / Kurzgeschichte (Kompiliert aus AsciiDoc)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
computerHistory $ terminal --session-closed (8 Threads // 8 GB RAM Ok)
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
:root {
|
||||
--bg-color: #0d1117;
|
||||
--text-color: #c9d1d9;
|
||||
--link-color: #58a6ff;
|
||||
--accent-color: #ff7b72;
|
||||
--border-color: #30363d;
|
||||
--code-bg: #161b22;
|
||||
--font-main: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-main);
|
||||
line-height: 1.6;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--accent-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 0.3em;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
h1 { font-size: 2.5em; border-bottom: 2px solid var(--accent-color); }
|
||||
h2 { font-size: 1.5em; }
|
||||
|
||||
a { color: var(--link-color); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; color: #79c0ff; }
|
||||
|
||||
code, kbd, pre {
|
||||
font-family: var(--font-main);
|
||||
background-color: var(--code-bg);
|
||||
color: #a5d6ff;
|
||||
border-radius: 3px;
|
||||
}
|
||||
code { padding: 0.2em 0.4em; }
|
||||
pre { padding: 16px; overflow: auto; border: 1px solid var(--border-color); }
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--accent-color);
|
||||
color: #8b949e;
|
||||
padding: 0 1em;
|
||||
margin-left: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 8px 12px;
|
||||
}
|
||||
th { background-color: var(--code-bg); color: var(--accent-color); }
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background-color: var(--border-color);
|
||||
border: none;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Name des lokalen Docker-Images
|
||||
IMAGE_NAME="lore-builder"
|
||||
IMAGE_NAME="ian-lore-builder"
|
||||
# Input Ordner
|
||||
INP_DIR=$(pwd)
|
||||
# Build Ordner
|
||||
# BID_DIR="90_builder"
|
||||
# Build templares
|
||||
# TEMPLATES="$BID_DIR/templates"
|
||||
# Ausgabeordner
|
||||
OUT_DIR="export"
|
||||
OUT_DIR="$INP_DIR/91_sonnensystem"
|
||||
|
||||
# Farben für Output
|
||||
GREEN='\033[0;32m'
|
||||
@@ -12,318 +18,31 @@ NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}=== Starte Master-Build-Prozess (Maschinen-Instanzen) ===${NC}"
|
||||
|
||||
# 1. Output-Verzeichnis erstellen
|
||||
# 1. Output-Verzeichnis löschen und erstellen
|
||||
rm -rf "$OUT_DIR"
|
||||
mkdir -p "$OUT_DIR"
|
||||
echo -e "Output-Ordner '${GREEN}$OUT_DIR${NC}' steht bereit."
|
||||
echo -e "Output-Ordner '${GREEN}$OUT_DIR${NC}' bereinigt und bereit."
|
||||
|
||||
# 2. Prüfen, ob das Docker-Image existiert, ansonsten on-the-fly bauen
|
||||
if [[ "$(docker images -q $IMAGE_NAME 2> /dev/null)" == "" ]]; then
|
||||
echo -e "${BLUE}Baue flüchtiges Docker-Image '$IMAGE_NAME' (Alpine, Asciidoctor, Pandoc)...${NC}"
|
||||
docker build -t $IMAGE_NAME - <<EOF
|
||||
FROM alpine:latest
|
||||
RUN apk add --no-cache asciidoctor pandoc bash
|
||||
WORKDIR /workspace
|
||||
EOF
|
||||
docker build -t $IMAGE_NAME -f ./90_builder/container/deb_Dockerfile .
|
||||
fi
|
||||
|
||||
# 3. CSS Datei für Retro Space Dark Theme in den Export-Ordner generieren
|
||||
cat << 'EOF' > "$OUT_DIR/retro-dark.css"
|
||||
:root {
|
||||
--bg-color: #0d1117;
|
||||
--text-color: #c9d1d9;
|
||||
--link-color: #58a6ff;
|
||||
--accent-color: #ff7b72;
|
||||
--border-color: #30363d;
|
||||
--code-bg: #161b22;
|
||||
--font-main: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-main);
|
||||
line-height: 1.6;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--accent-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 0.3em;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
h1 { font-size: 2.5em; border-bottom: 2px solid var(--accent-color); }
|
||||
h2 { font-size: 1.5em; }
|
||||
|
||||
a { color: var(--link-color); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; color: #79c0ff; }
|
||||
|
||||
code, kbd, pre {
|
||||
font-family: var(--font-main);
|
||||
background-color: var(--code-bg);
|
||||
color: #a5d6ff;
|
||||
border-radius: 3px;
|
||||
}
|
||||
code { padding: 0.2em 0.4em; }
|
||||
pre { padding: 16px; overflow: auto; border: 1px solid var(--border-color); }
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--accent-color);
|
||||
color: #8b949e;
|
||||
padding: 0 1em;
|
||||
margin-left: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 8px 12px;
|
||||
}
|
||||
th { background-color: var(--code-bg); color: var(--accent-color); }
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background-color: var(--border-color);
|
||||
border: none;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
EOF
|
||||
# move to hard static $BID_OUT/templates/retro-dark.css
|
||||
|
||||
echo -e "${BLUE}Starte Build-Container und generiere Dokumente...${NC}"
|
||||
|
||||
# 4. Container starten und Build-Schritte im Container ausführen
|
||||
docker run --rm -v "$(pwd):/workspace" $IMAGE_NAME bash -c "
|
||||
function convert_adoc() {
|
||||
if [ -f \"\$1\" ]; then
|
||||
echo \"-> Konvertiere \$1 zu HTML...\"
|
||||
asciidoctor -b html5 -a stylesheet=retro-dark.css -a linkcss -a toc=left \"\$1\" -o \"$OUT_DIR/\$2\"
|
||||
else
|
||||
echo \" [INFO] \$1 nicht gefunden, überspringe...\"
|
||||
fi
|
||||
}
|
||||
|
||||
echo '--- Baue README ---'
|
||||
convert_adoc README.adoc README.html
|
||||
|
||||
echo '--- Baue Lore (Master-Dokument) ---'
|
||||
LORE_SRC=\"index.adoc\"
|
||||
if [ -f master.adoc ]; then LORE_SRC=\"master.adoc\"; fi
|
||||
|
||||
if [ -f \"\$LORE_SRC\" ]; then
|
||||
echo \"-> Generiere Lore-Dokument (HTML & Markdown) aus \$LORE_SRC...\"
|
||||
asciidoctor -b html5 -d book -a stylesheet=retro-dark.css -a linkcss -a toc=left \"\$LORE_SRC\" -o \"$OUT_DIR/lore.html\"
|
||||
asciidoctor -b docbook -d book \"\$LORE_SRC\" -o \"$OUT_DIR/temp_lore.xml\"
|
||||
pandoc -f docbook -t markdown_strict \"$OUT_DIR/temp_lore.xml\" -o \"$OUT_DIR/lore.md\"
|
||||
rm \"$OUT_DIR/temp_lore.xml\"
|
||||
else
|
||||
echo \" [WARNUNG] Weder index.adoc noch master.adoc gefunden!\"
|
||||
fi
|
||||
|
||||
echo '--- Baue Fließtexte / Geschichten ---'
|
||||
if [ -f darkCenter.adoc ]; then
|
||||
echo \"-> Konvertiere darkCenter.adoc zu HTML...\"
|
||||
asciidoctor -b html5 -d book -a stylesheet=retro-dark.css -a linkcss -a toc=left darkCenter.adoc -o \"$OUT_DIR/darkCenter.html\"
|
||||
fi
|
||||
|
||||
if [ -f masterOfTheWorld-sf.md ]; then
|
||||
echo \"-> Konvertiere masterOfTheWorld-sf.md zu HTML...\"
|
||||
pandoc masterOfTheWorld-sf.md -f markdown -t html -s -c retro-dark.css -o \"$OUT_DIR/masterOfTheWorld-sf.html\"
|
||||
fi
|
||||
"
|
||||
# docker run --rm -v "$INP_DIR:/workspace" $IMAGE_NAME /workspace/bookFold.sh
|
||||
docker run --rm -v "$(pwd):/workspace/input" -v "$(pwd)/91_sonnensystem:/workspace/91_sonnensystem" $IMAGE_NAME
|
||||
|
||||
# 5. index.html vollautomatisch und direkt generieren
|
||||
# moved to $BID_OUT/templates/index.html
|
||||
|
||||
echo -e "${BLUE}Generiere taktisches Dashboard ('$OUT_DIR/index.html')...${NC}"
|
||||
|
||||
cat << 'EOF' > "$OUT_DIR/index.html"
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CORE // MASCHINEN-INSTANZEN_LOG</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #0d1117;
|
||||
--text-color: #c9d1d9;
|
||||
--link-color: #58a6ff;
|
||||
--accent-color: #ff7b72;
|
||||
--border-color: #30363d;
|
||||
--code-bg: #161b22;
|
||||
--font-main: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-main);
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
color: var(--accent-color);
|
||||
font-size: 2em;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.system-status {
|
||||
font-size: 0.9em;
|
||||
color: #8b949e;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.status-blink {
|
||||
color: #7ee787;
|
||||
animation: blink 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
.category-block {
|
||||
border: 1px solid var(--border-color);
|
||||
background-color: var(--code-bg);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
color: var(--accent-color);
|
||||
font-size: 1.2em;
|
||||
text-transform: uppercase;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.8rem;
|
||||
padding-left: 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
li::before {
|
||||
content: ">>";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
font-size: 0.85em;
|
||||
color: #8b949e;
|
||||
display: block;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 4rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 1rem;
|
||||
font-size: 0.8em;
|
||||
color: #8b949e;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1 class="terminal-title">Maschinen-Instanzen // Datenarchiv</h1>
|
||||
<div class="system-status">
|
||||
STATUS: <span class="status-blink">ONLINE</span> | REPO: UBERSPACE/GITEA | NODE: ACER_SPIN5_CLIENT
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="category-block">
|
||||
<h2 class="category-title">00 // System-Konfiguration</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="README.html">README.html</a>
|
||||
<span class="file-meta">System-Handbuch, Keymaps, Sync-Architektur & Befehle</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="category-block">
|
||||
<h2 class="category-title">01 // Datenspeicher (Lore-Bibel)</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="lore.html">lore.html</a>
|
||||
<span class="file-meta">Das vollständige Master-Dokument (Kompiliertes Buch-Format)</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="lore.md">lore.md</a>
|
||||
<span class="file-meta">Rohdaten-Export im Markdown-Format (LLM / Gitea kompatibel)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="category-block">
|
||||
<h2 class="category-title">02 // Chroniken & Vektor-Flüge</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="darkCenter.html">darkCenter.html</a>
|
||||
<span class="file-meta">Fließtext / Kurzgeschichte (Kompiliert aus AsciiDoc)</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="masterOfTheWorld-sf.html">masterOfTheWorld-sf.html</a>
|
||||
<span class="file-meta">Fließtext / Kurzgeschichte (Konvertiert aus Markdown)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
computerHistory $ terminal --session-closed (8 Threads // 8 GB RAM Ok)
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}=== Build erfolgreich! ===${NC}"
|
||||
echo "Alle Dokumente (inkl. Dashboard) liegen im Ordner './$OUT_DIR' bereit."
|
||||
|
||||
Reference in New Issue
Block a user