diff --git a/.gitignore b/.gitignore
index 99e8b76..321606f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
# Self-contained NUI-Vorschau, gebaut von .tools/nui-preview.mjs
nui/_preview.html
+
+# Kontrollblatt, gebaut von icons/gen-icons.mjs
+icons/_check.html
diff --git a/README.md b/README.md
index 07ce857..af4eac6 100644
--- a/README.md
+++ b/README.md
@@ -61,23 +61,28 @@ weich ein und aus.
und in `ox_inventory/data/items.lua` innerhalb der `return { … }`-Klammer
einfügen. Sechs Items, nichts weiter anzupassen.
-3. Icons nach `cdn.d4rkst3r.de/items/` legen: `diving_gear.png`,
- `diving_fill.png`, `diving_tank_small.png`, `diving_tank_medium.png`,
- `diving_tank_large.png`, `diving_tank_xl.png`.
+3. Die sechs PNGs aus [`icons/`](icons) nach `cdn.d4rkst3r.de/items/` hochladen.
+ Sie heißen genau wie die Items, deshalb braucht es kein `client.image`.
Es gibt bewusst **keine** fertige `items.lua` zum Ersetzen — die würde die
restlichen Items des Servers überschreiben.
## Items
-| Item | Label | Laufzeit | Fülldruck | Gewicht |
-|---|---|---|---|---|
-| `diving_gear` | Tauchausrüstung | — | — | 5000 |
-| `diving_fill` | Pressluft-Kartusche | — | — | 1000 |
-| `diving_tank_small` | Tauchflasche (5 Min) | 300 s | 300 bar | 4000 |
-| `diving_tank_medium` | Tauchflasche (10 Min) | 600 s | 300 bar | 7000 |
-| `diving_tank_large` | Tauchflasche (15 Min) | 900 s | 300 bar | 10000 |
-| `diving_tank_xl` | Tauchflasche (20 Min) | 1200 s | 300 bar | 13000 |
+| Icon | Item | Label | Laufzeit | Fülldruck | Gewicht |
+|---|---|---|---|---|---|
+|
| `diving_gear` | Tauchausrüstung | — | — | 5000 |
+|
| `diving_fill` | Pressluft-Kartusche | — | — | 1000 |
+|
| `diving_tank_small` | Tauchflasche (5 Min) | 300 s | 300 bar | 4000 |
+|
| `diving_tank_medium` | Tauchflasche (10 Min) | 600 s | 300 bar | 7000 |
+|
| `diving_tank_large` | Tauchflasche (15 Min) | 900 s | 300 bar | 10000 |
+|
| `diving_tank_xl` | Tauchflasche (20 Min) | 1200 s | 300 bar | 13000 |
+
+Die Icons liegen als 128×128 PNG mit transparentem Hintergrund in
+[`icons/`](icons), die SVG-Quellen in `icons/src/`. Die Laufzeit steht als Zahl
+auf dem Farbband — ohne die sind die Einzelflaschen im Inventar nur noch an der
+Bandfarbe auseinanderzuhalten. Wer `capacity` ändert, sollte die Zahlen in
+`icons/gen-icons.mjs` mitziehen.
Dass alle Flaschen mit demselben Fülldruck starten, ist gewollt: in echt macht das
Volumen die Laufzeit, nicht der Druck. Die große Flasche hält 300 bar einfach länger.
@@ -197,8 +202,22 @@ solcher Punkte enthält also den Pitch.
node ../.tools/lint.mjs # Lua-Lint, CfxLua-tauglich
node ../.tools/nui-preview.mjs nui # HUD im Browser anschauen
node docs/gen-hud-svg.mjs docs # Renderings für diese README neu bauen
+
+# Item-Icons neu bauen (SVG -> PNG, braucht ImageMagick mit librsvg)
+node icons/gen-icons.mjs
+for f in icons/src/*.svg; do
+ magick -background none "$f" -resize 128x128 "icons/$(basename "$f" .svg).png"
+done
```
+`gen-icons.mjs` schreibt außerdem `icons/_check.html` — alle Icons auf
+Schachbrett, in Slot-Größe und klein, zum Gegenschauen nach Änderungen.
+
+> librsvg 2.40 wirft beim Rendern der Zahlen ein
+> `Invalid UTF-8 string passed to pango_layout_set_text()`. Das ist harmlos und
+> passiert bei jedem ``-Element, auch im minimalsten Testfall — die Zahlen
+> kommen korrekt raus.
+
Der Umbau ist in [PLAN.md](PLAN.md) dokumentiert, inklusive der offenen Punkte.
## Bekannte Unsicherheiten
diff --git a/icons/diving_fill.png b/icons/diving_fill.png
new file mode 100644
index 0000000..d419cdc
Binary files /dev/null and b/icons/diving_fill.png differ
diff --git a/icons/diving_gear.png b/icons/diving_gear.png
new file mode 100644
index 0000000..3bc5d6a
Binary files /dev/null and b/icons/diving_gear.png differ
diff --git a/icons/diving_tank_large.png b/icons/diving_tank_large.png
new file mode 100644
index 0000000..c013931
Binary files /dev/null and b/icons/diving_tank_large.png differ
diff --git a/icons/diving_tank_medium.png b/icons/diving_tank_medium.png
new file mode 100644
index 0000000..c11ba02
Binary files /dev/null and b/icons/diving_tank_medium.png differ
diff --git a/icons/diving_tank_small.png b/icons/diving_tank_small.png
new file mode 100644
index 0000000..53af1c5
Binary files /dev/null and b/icons/diving_tank_small.png differ
diff --git a/icons/diving_tank_xl.png b/icons/diving_tank_xl.png
new file mode 100644
index 0000000..dfe3d2e
Binary files /dev/null and b/icons/diving_tank_xl.png differ
diff --git a/icons/gen-icons.mjs b/icons/gen-icons.mjs
new file mode 100644
index 0000000..45f699e
--- /dev/null
+++ b/icons/gen-icons.mjs
@@ -0,0 +1,175 @@
+#!/usr/bin/env node
+// Erzeugt die Item-Icons als SVG. Die PNGs macht danach ImageMagick daraus:
+//
+// node icons/gen-icons.mjs
+// for f in icons/src/*.svg; do magick -background none "$f" -resize 128x128 "icons/$(basename "$f" .svg).png"; done
+//
+// Nur SVG 1.1 mit Presentation-Attributen, keine CSS-Bloecke und keine Filter -
+// gerendert wird mit librsvg 2.40, und das ist alt.
+//
+// librsvg 2.40 wirft bei jedem -Element ein
+// WARNING: Invalid UTF-8 string passed to pango_layout_set_text()
+// Das ist harmlos und tritt auch beim minimalsten Testfall auf - die Zahlen auf
+// den Flaschen rendern korrekt. Nachgeprueft, nicht ignoriert.
+//
+// Erzeugt nebenbei icons/_check.html: alle Icons auf Schachbrett, in Slot-Groesse
+// und klein, zum Gegenschauen nach Aenderungen.
+
+import { writeFileSync, mkdirSync } from 'node:fs';
+import { join, dirname } from 'node:path';
+
+const OUT = join(process.argv[2] ?? 'icons', 'src');
+mkdirSync(OUT, { recursive: true });
+
+const S = 128; // Canvas, quadratisch
+
+const C = {
+ steelLight: '#cbd5e1',
+ steel: '#94a3b8',
+ steelDark: '#64748b',
+ valve: '#334155',
+ valveDark: '#1e293b',
+ glass: '#38bdf8',
+ glassDark: '#0284c7',
+ rubber: '#1e293b',
+ outline: '#0f172a'
+};
+
+/** Farbband pro Groesse, damit die Flaschen im Inventar auf einen Blick unterscheidbar sind. */
+const BANDS = {
+ small: '#4ade80',
+ medium: '#38bdf8',
+ large: '#fbbf24',
+ xl: '#f472b6'
+};
+
+const defs = `
+
+
+
+
+
+
+
+
+
+
+ `;
+
+const svg = (body) =>
+ `
+`;
+
+/**
+ * Eine Tauchflasche. cx = Mittelachse, w = Breite, top/bottom = Koerper in y.
+ * minutes = Laufzeit aufs Band gedruckt. Ohne die Zahl sind die Einzelflaschen
+ * bei kleiner Darstellung nur noch an der Bandfarbe auseinanderzuhalten.
+ */
+function tank(cx, w, top, bottom, band, minutes) {
+ const x = cx - w / 2;
+ const h = bottom - top;
+ const valveW = w * 0.4;
+ const bandY = top + h * 0.34;
+ const bandH = 22;
+
+ const label = minutes
+ ? `${minutes}`
+ : '';
+
+ return `
+
+
+
+
+
+ ${label}
+ `;
+}
+
+// --- Flaschen: gleiche Breite, wachsende Hoehe. Die Silhouette traegt die Info,
+// --- das Farbband hilft zusaetzlich.
+const sizes = {
+ diving_tank_small: { w: 42, top: 48, bottom: 114, band: BANDS.small, minutes: 5 },
+ diving_tank_medium: { w: 46, top: 36, bottom: 116, band: BANDS.medium, minutes: 10 },
+ diving_tank_large: { w: 50, top: 26, bottom: 118, band: BANDS.large, minutes: 15 }
+};
+
+for (const [name, cfg] of Object.entries(sizes)) {
+ writeFileSync(join(OUT, `${name}.svg`), svg(tank(64, cfg.w, cfg.top, cfg.bottom, cfg.band, cfg.minutes)));
+}
+
+// --- XL = Doppelflasche, passend zum Label. Die Zahl steht auf einem Band
+// --- ueber beide Flaschen, sonst muesste sie zweimal auftauchen.
+writeFileSync(join(OUT, 'diving_tank_xl.svg'), svg(
+ `
+${tank(46, 38, 38, 118, BANDS.xl)}
+${tank(82, 38, 38, 118, BANDS.xl)}
+
+ 20`
+));
+
+// --- Tauchmaske
+writeFileSync(join(OUT, 'diving_gear.svg'), svg(
+ `
+
+
+
+
+
+
+ `
+));
+
+// --- Pressluft-Kartusche: liegende, gedrungene Form mit Manometer. Bewusst quer
+// --- statt hochkant, damit sie sich auf einen Blick von den Flaschen abhebt.
+writeFileSync(join(OUT, 'diving_fill.svg'), svg(
+ `
+
+
+
+
+
+
+
+
+ `
+));
+
+// --- Kontrollblatt
+const NAMES = [
+ 'diving_gear', 'diving_fill',
+ 'diving_tank_small', 'diving_tank_medium', 'diving_tank_large', 'diving_tank_xl'
+];
+
+const slots = (extra = '') =>
+ NAMES.map((n) => ``).join('');
+
+writeFileSync(join(dirname(OUT), '_check.html'), `
+Transparenz
+${slots()}
+Inventar-Slot 100px
+${slots()}
+Klein 56px
+${slots()}
+`);
+
+console.log('SVGs in', OUT, '+ _check.html');
diff --git a/icons/src/diving_fill.svg b/icons/src/diving_fill.svg
new file mode 100644
index 0000000..65a592c
--- /dev/null
+++ b/icons/src/diving_fill.svg
@@ -0,0 +1,24 @@
+
diff --git a/icons/src/diving_gear.svg b/icons/src/diving_gear.svg
new file mode 100644
index 0000000..8bde5c6
--- /dev/null
+++ b/icons/src/diving_gear.svg
@@ -0,0 +1,24 @@
+
diff --git a/icons/src/diving_tank_large.svg b/icons/src/diving_tank_large.svg
new file mode 100644
index 0000000..a5d6b9f
--- /dev/null
+++ b/icons/src/diving_tank_large.svg
@@ -0,0 +1,23 @@
+
diff --git a/icons/src/diving_tank_medium.svg b/icons/src/diving_tank_medium.svg
new file mode 100644
index 0000000..cc10565
--- /dev/null
+++ b/icons/src/diving_tank_medium.svg
@@ -0,0 +1,23 @@
+
diff --git a/icons/src/diving_tank_small.svg b/icons/src/diving_tank_small.svg
new file mode 100644
index 0000000..2c9b09e
--- /dev/null
+++ b/icons/src/diving_tank_small.svg
@@ -0,0 +1,23 @@
+
diff --git a/icons/src/diving_tank_xl.svg b/icons/src/diving_tank_xl.svg
new file mode 100644
index 0000000..4ce4562
--- /dev/null
+++ b/icons/src/diving_tank_xl.svg
@@ -0,0 +1,34 @@
+
diff --git a/items_for_ox_inventory.lua b/items_for_ox_inventory.lua
index bcabb84..5e58685 100644
--- a/items_for_ox_inventory.lua
+++ b/items_for_ox_inventory.lua
@@ -8,6 +8,10 @@
-- Bewusst KEINE fertige items.lua zum Ersetzen: die würde die restlichen Items
-- des Servers mitlöschen.
--
+-- Kein client.image gesetzt: ox_inventory lädt /.png, und die
+-- Icons in icons/ heißen genau wie die Items. Einfach nach cdn.d4rkst3r.de/items/
+-- hochladen, fertig.
+--
-- WICHTIG: kein `decay = true` auf den Flaschen. Die durability trägt hier den
-- Füllstand, und ox_inventory löscht Items mit decay, sobald durability 0 erreicht
-- (modules/items/server.lua) - die leere Flasche würde also verschwinden statt
@@ -23,8 +27,7 @@
stack = false,
close = true,
consume = 0,
- description = 'Maske und Tauchanzug. Benutzen zum An- und Ausziehen.',
- client = { image = 'diving_gear.png' }
+ description = 'Maske und Tauchanzug. Benutzen zum An- und Ausziehen.'
},
['diving_fill'] = {
@@ -33,8 +36,7 @@
stack = true,
close = true,
consume = 0,
- description = 'Füllt eine Tauchflasche wieder auf.',
- client = { image = 'diving_fill.png' }
+ description = 'Füllt eine Tauchflasche wieder auf.'
},
['diving_tank_small'] = {
@@ -43,8 +45,7 @@
stack = false,
close = true,
consume = 0,
- description = 'Kleine Tauchflasche, 300 bar. Reicht für etwa 5 Minuten.',
- client = { image = 'diving_tank_small.png' }
+ description = 'Kleine Tauchflasche, 300 bar. Reicht für etwa 5 Minuten.'
},
['diving_tank_medium'] = {
@@ -53,8 +54,7 @@
stack = false,
close = true,
consume = 0,
- description = 'Mittlere Tauchflasche, 300 bar. Reicht für etwa 10 Minuten.',
- client = { image = 'diving_tank_medium.png' }
+ description = 'Mittlere Tauchflasche, 300 bar. Reicht für etwa 10 Minuten.'
},
['diving_tank_large'] = {
@@ -63,8 +63,7 @@
stack = false,
close = true,
consume = 0,
- description = 'Große Tauchflasche, 300 bar. Reicht für etwa 15 Minuten.',
- client = { image = 'diving_tank_large.png' }
+ description = 'Große Tauchflasche, 300 bar. Reicht für etwa 15 Minuten.'
},
['diving_tank_xl'] = {
@@ -73,6 +72,5 @@
stack = false,
close = true,
consume = 0,
- description = 'Doppelflasche, 300 bar. Reicht für etwa 20 Minuten.',
- client = { image = 'diving_tank_xl.png' }
+ description = 'Doppelflasche, 300 bar. Reicht für etwa 20 Minuten.'
},
diff --git a/t1.png b/t1.png
new file mode 100644
index 0000000..c299927
Binary files /dev/null and b/t1.png differ
diff --git a/t1.svg b/t1.svg
new file mode 100644
index 0000000..1ca9fe7
--- /dev/null
+++ b/t1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/t2.png b/t2.png
new file mode 100644
index 0000000..c299927
Binary files /dev/null and b/t2.png differ
diff --git a/t2.svg b/t2.svg
new file mode 100644
index 0000000..2c328c5
--- /dev/null
+++ b/t2.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file