Wetier am Cahstity game gebasterln

This commit is contained in:
2026-03-17 19:51:51 +01:00
parent 97c6f0a131
commit aafc203407
130 changed files with 9356 additions and 4222 deletions

View File

@@ -0,0 +1,70 @@
/**
* Zentrale Kartendefinitionen für das Chastity Game.
*
* Exportiert (global):
* CARD_DEFS Array mit { id, img, name, desc, defMin, defMax }
* CARD_LABELS Object { ID: { name, img, desc } } (Lookup für card-display.js u.a.)
*/
const CARD_DEFS = [
{
id: 'RED',
img: '/img/card_red.png',
name: 'Rote Karte',
desc: 'Niete - Viel Erfolg beim nächsten Zug',
defMin: 5,
defMax: 10,
},
{
id: 'GREEN',
img: '/img/card_green.png',
name: 'Grüne Karte',
desc: 'Öffnet das Lock. Kann wieder ins Deck zurück gelegt werden',
defMin: 1,
defMax: 2,
},
{
id: 'YELLOW',
img: '/img/card_yellow.png',
name: 'Gelbe Karte',
desc: 'Per Zufall werden rote Karten entfernt oder hinzugefügt',
defMin: 1,
defMax: 2,
},
{
id: 'TASK',
img: '/img/card_task.png',
name: 'Aufgabe',
desc: 'Keyholder*In, Community oder der Zufall teilt eine Aufgabe zu.',
defMin: 0,
defMax: 0,
},
{
id: 'FREEZE',
img: '/img/card_freeze.png',
name: 'Freeze',
desc: 'Friert das Lock für eine festgelegte Zeit ein in diesem Zeitraum können keine Karten gezogen werden.',
defMin: 0,
defMax: 0,
},
{
id: 'RESET',
img: '/img/card_reset.png',
name: 'Reset',
desc: 'Setzt das Kartendeck auf den Ausgangszustand zurück. Alle bisher gezogenen Karten kommen wieder rein.',
defMin: 0,
defMax: 0,
},
{
id: 'DOUBLE_UP',
img: '/img/card_doubleup.png',
name: 'Double Up',
desc: 'Verdoppelt alle noch im Deck vorhandenen Karten.',
defMin: 0,
defMax: 0,
},
];
/** Lookup-Objekt für Konsumenten, die nach ID auf Name/Bild/Beschreibung zugreifen. */
const CARD_LABELS = Object.fromEntries(
CARD_DEFS.map(c => [c.id, { name: c.name, img: c.img, desc: c.desc }])
);

View File

@@ -1,6 +1,7 @@
/**
* Gemeinsame Kartenanzeige für Chastity Game.
* Exportiert: CARD_LABELS, cardTypeGridHtml(cardCounts)
* Benötigt: /js/card-defs.js (CARD_LABELS muss bereits global verfügbar sein)
* Exportiert: cardTypeGridHtml(cardCounts)
*/
(function () {
const style = document.createElement('style');
@@ -35,16 +36,6 @@
document.head.appendChild(style);
})();
const CARD_LABELS = {
RED: { name: 'Rote Karte', img: '/img/card_red.png', desc: 'Verlängert die Sperrzeit. Je nach Konfiguration werden Minuten oder Stunden auf den Timer addiert.' },
GREEN: { name: 'Grüne Karte', img: '/img/card_green.png', desc: 'Verkürzt die Sperrzeit. Eine grüne Karte bringt dich dem Öffnen näher.' },
YELLOW: { name: 'Gelbe Karte', img: '/img/card_yellow.png', desc: 'Neutrales Ereignis keine Zeitveränderung, aber es passiert trotzdem etwas.' },
TASK: { name: 'Aufgabe', img: '/img/card_task.png', desc: 'Teilt dir eine zufällige Aufgabe aus der Aufgabenliste zu. Die Aufgabe muss erfüllt werden.' },
FREEZE: { name: 'Freeze', img: '/img/card_freeze.png', desc: 'Friert das Lock für eine festgelegte Zeit ein in diesem Zeitraum können keine Karten gezogen werden.' },
RESET: { name: 'Reset', img: '/img/card_reset.png', desc: 'Setzt das Kartendeck auf den Ausgangszustand zurück. Alle bisher gezogenen Karten kommen wieder rein.' },
DOUBLE_UP: { name: 'Double Up', img: '/img/card_doubleup.png', desc: 'Verdoppelt alle Karten im aktuellen Deck.' },
};
/**
* Gibt HTML für ein Karten-Typ-Raster zurück (ein Bild pro Typ, Anzahl-Badge).
* @param {Object} cardCounts { RED: 3, GREEN: 1, … }

View File

@@ -27,11 +27,12 @@
icon: '⊗',
items: [
{ href: '/infochastity.html', icon: '', label: 'Info' },
{ href: '/sessionchastity.html', icon: '▷', label: 'Neues Lock', id: 'navChastityNeu' },
{ href: '/neulock.html', icon: '▷', label: 'Neues Lock', id: 'navChastityNeu' },
{ href: '#', icon: '▶', label: 'Aktives Lock', id: 'navChastityAktiv' },
{ href: '/communityvotes.html', icon: '🗳️', label: 'Community Votes' },
{ href: '/meine-locks.html', icon: '🔒', label: 'Meine Locks' },
{ href: '/keyholder.html', icon: '🔑', label: 'Keyholder' },
{ href: '/unlock-history.html', icon: '🗝️', label: 'Code-Historie' },
]
},
];
@@ -125,7 +126,7 @@
const lockId = lockData.lockId;
if (navCAktiv) {
navCAktiv.style.display = '';
navCAktiv.querySelector('a').href = '/sessionchastityingame.html?lockId=' + lockId;
navCAktiv.querySelector('a').href = '/activelock.html?lockId=' + lockId;
}
}
} catch (_) { /* Menü bleibt im Standardzustand */ }

View File

@@ -5,12 +5,13 @@
const path = window.location.pathname;
const links = [
{ href: '/feed.html', icon: '📰', label: 'Feed', badgeId: null, mobileBadgeId: null },
{ href: '/personen-suchen.html', icon: '⊕', label: 'Personen suchen', badgeId: null, mobileBadgeId: null },
{ href: '/freunde.html', icon: '♡', label: 'Freunde', badgeId: 'socialFriendsBadge', mobileBadgeId: 'socialMobileFriendsBadge' },
{ href: '/nachrichten.html', icon: '✉', label: 'Nachrichten', badgeId: 'socialMsgBadge', mobileBadgeId: 'socialMobileMsgBadge' },
{ href: '/gruppen.html', icon: '👥', label: 'Gruppen', badgeId: 'socialGruppenBadge', mobileBadgeId: 'socialMobileGruppenBadge' },
{ href: '/einladungen.html', icon: '', label: 'Einladungen', badgeId: 'socialInvBadge', mobileBadgeId: 'socialMobileInvBadge' },
{ href: '/feed.html', icon: '📰', label: 'Feed', badgeId: null, mobileBadgeId: null },
{ href: '/personen-suchen.html', icon: '⊕', label: 'Personen suchen', badgeId: null, mobileBadgeId: null },
{ href: '/freunde.html', icon: '♡', label: 'Freunde', badgeId: 'socialFriendsBadge', mobileBadgeId: 'socialMobileFriendsBadge' },
{ href: '/nachrichten.html', icon: '✉', label: 'Nachrichten', badgeId: 'socialMsgBadge', mobileBadgeId: 'socialMobileMsgBadge' },
{ href: '/benachrichtigungen.html', icon: '🔔', label: 'Benachrichtigungen', badgeId: 'socialNotifBadge', mobileBadgeId: 'socialMobileNotifBadge' },
{ href: '/gruppen.html', icon: '👥', label: 'Gruppen', badgeId: 'socialGruppenBadge', mobileBadgeId: 'socialMobileGruppenBadge' },
{ href: '/einladungen.html', icon: '✉', label: 'Einladungen', badgeId: 'socialInvBadge', mobileBadgeId: 'socialMobileInvBadge' },
];
const profileActive = (path === '/benutzer.html' || path === '/profile.html') ? ' class="active"' : '';
@@ -102,6 +103,23 @@
});
}
// ── Ton abspielen ──
// Browser erlauben audio.play() sobald der Nutzer mindestens einmal interagiert hat.
let userHasInteracted = false;
document.addEventListener('click', () => { userHasInteracted = true; }, { passive: true });
document.addEventListener('keydown', () => { userHasInteracted = true; }, { passive: true });
document.addEventListener('touchstart', () => { userHasInteracted = true; }, { passive: true });
function playSound(src) {
if (!userHasInteracted) return;
try {
const audio = new Audio(src);
audio.volume = 0.6;
audio.play().catch(() => {});
} catch(e) {}
}
// ── Initiale Badge-Counts laden ──
fetch('/social/friends/pending/count')
.then(r => r.ok ? r.json() : 0)
.then(n => setBadge(['socialFriendsBadge', 'socialMobileFriendsBadge'], n))
@@ -112,6 +130,11 @@
.then(n => setBadge(['socialMsgBadge', 'socialMobileMsgBadge'], n))
.catch(() => {});
fetch('/notifications/unread/count')
.then(r => r.ok ? r.json() : 0)
.then(n => setBadge(['socialNotifBadge', 'socialMobileNotifBadge'], n))
.catch(() => {});
Promise.all([
fetch('/gruppen/requests/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0),
fetch('/gruppen/reports/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0)
@@ -124,4 +147,41 @@
]).then(([khInvs, lockeeInvs]) =>
setBadge(['socialInvBadge', 'socialMobileInvBadge'], khInvs.length + lockeeInvs.length)
).catch(() => {});
// ── SSE: Echtzeit-Push vom Server ──
function connectSse() {
const es = new EventSource('/events/stream');
es.addEventListener('DM', e => {
try {
const data = JSON.parse(e.data);
setBadge(['socialMsgBadge', 'socialMobileMsgBadge'], data.unreadCount || 0);
// Nur Ton abspielen wenn nicht gerade auf der Nachrichten-Seite
if (window.location.pathname !== '/nachrichten.html') {
playSound('/audio/message.mp3');
}
// Nachrichten-Seite: sofortiges Laden neuer Nachrichten auslösen
if (typeof window.__sseOnDm === 'function') window.__sseOnDm(data);
} catch(ex) {}
});
es.addEventListener('NOTIFICATION', e => {
try {
const data = JSON.parse(e.data);
setBadge(['socialNotifBadge', 'socialMobileNotifBadge'], data.unreadCount || 0);
if (window.location.pathname !== '/benachrichtigungen.html') {
playSound('/audio/notification.mp3');
}
if (typeof window.__sseOnNotification === 'function') window.__sseOnNotification(data);
} catch(ex) {}
});
es.onerror = () => {
es.close();
// Nach 5 Sekunden neu verbinden
setTimeout(connectSse, 5000);
};
}
connectSse();
})();