feat(bisaya-course): expand exercises for shopping, neighborhood visits, conflict resolution, and free speaking
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
All checks were successful
Deploy to production / deploy (push) Successful in 2m57s
- Added new exercises in multiple-choice, gap-fill, and situational response formats for categories including 'Einkaufen vertiefen', 'Nachbarschaft & Besuche', 'Rollenspiel - Konflikt und Hilfe', and 'Freies Sprechen - Alltag ohne Stütze'. - Each exercise includes detailed instructions, question data, answer data, and explanations to enhance the learning experience for Bisaya language learners. - Focused on practical scenarios to improve conversational skills and vocabulary retention.
This commit is contained in:
@@ -2136,6 +2136,534 @@ const BISAYA_EXERCISES = {
|
||||
},
|
||||
explanation: 'Die Abschlussprüfung bündelt Weg, Organisation und Hilfe in einer letzten Miniszene.'
|
||||
})
|
||||
],
|
||||
|
||||
'Einkaufen vertiefen': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Gesamtpreis erfragen',
|
||||
instruction: 'Wähle die passendste Einkaufsfrage.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Du hast mehrere Dinge ausgesucht. Wie fragst du nach dem Gesamtpreis?',
|
||||
options: ['Pila ni tanan?', 'Kapoy na ka?', 'Asa ang bata?', 'Tabangan tika.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Pila ni tanan?" fragt nach dem Gesamtpreis.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Menge ergänzen',
|
||||
instruction: 'Fülle die Lücke mit dem passenden Mengenwort.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: 'Pwede tulo ka{gap}?',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['buok']
|
||||
},
|
||||
explanation: '"buok" wird für zählbare Einzelstücke verwendet.'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Einkauf abschließen',
|
||||
instruction: 'Reagiere passend beim Bezahlen.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Frage nach dem Gesamtpreis und sage dann, dass du es nimmst.',
|
||||
keywords: ['tanan', 'kuhaon']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Pila ni tanan? Kuhaon na nako.',
|
||||
keywords: ['tanan', 'kuhaon']
|
||||
},
|
||||
explanation: 'Das ist ein sehr alltagsnaher Miniabschluss beim Einkaufen.'
|
||||
})
|
||||
],
|
||||
|
||||
'Nachbarschaft & Besuche': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Bei Nachbarn vorbeischauen',
|
||||
instruction: 'Wähle die passendste Aussage für einen Besuch bei Nachbarn.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Wie sagst du: "Wir waren bei den Nachbarn"?',
|
||||
options: ['Niadto mi sa silingan.', 'Adto ta sa doktor.', 'Magdula ta.', 'Naa koy assignment.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"silingan" bedeutet Nachbar oder Nachbarschaft.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Einladung in die Nachbarschaft',
|
||||
instruction: 'Fülle die Lücke mit dem passenden Besuchswort.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: '{gap} mo unya.',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['Bisita']
|
||||
},
|
||||
explanation: '"Bisita mo unya." lädt zu einem späteren Besuch ein.'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Nachbarschaftsbesuch ankündigen',
|
||||
instruction: 'Reagiere in zwei kurzen Sätzen.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Sag, dass ihr bei den Nachbarn wart und dass sie später zu Besuch kommen können.',
|
||||
keywords: ['silingan', 'bisita']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Niadto mi sa silingan. Bisita mo unya.',
|
||||
keywords: ['silingan', 'bisita']
|
||||
},
|
||||
explanation: 'Das verbindet Begegnung und Einladung in einem natürlichen Nachbarschaftskontext.'
|
||||
})
|
||||
],
|
||||
|
||||
'Rollenspiel - Konflikt und Hilfe': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Konflikt ruhig eröffnen',
|
||||
instruction: 'Wähle die höflichste Eröffnung für ein schwieriges Gespräch.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Wie leitest du ein Konfliktgespräch ruhig ein?',
|
||||
options: ['Pwede nato istoryahan?', 'Pila ni tanan?', 'Sulod lang.', 'Nikaon na ka?']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Pwede nato istoryahan?" ist weich und gesprächsorientiert.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Hilfe ergänzen',
|
||||
instruction: 'Fülle die Lücke mit der passenden Hilfeformel.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: '{gap} tika.',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['Tabangan']
|
||||
},
|
||||
explanation: '"Tabangan tika." bedeutet "Ich helfe dir."'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Konflikt und Hilfe verbinden',
|
||||
instruction: 'Reagiere kurz, höflich und lösungsorientiert.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Bitte darum, das Problem zu besprechen, und biete anschließend Hilfe an.',
|
||||
keywords: ['istoryahan', 'tabangan']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Pwede nato istoryahan? Tabangan tika.',
|
||||
keywords: ['istoryahan', 'tabangan']
|
||||
},
|
||||
explanation: 'Das Rollenspiel verbindet Deeskalation und konkrete Hilfe.'
|
||||
})
|
||||
],
|
||||
|
||||
'Freies Sprechen - Alltag ohne Stütze': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Freies Sprechen strukturieren',
|
||||
instruction: 'Wähle den Ausdruck, mit dem du eine freie Aussage natürlich einleitest.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Welche Form passt gut als Einleitung für freies Sprechen?',
|
||||
options: ['Sa tinuod...', 'Pila ang plite?', 'Asa ang sakayan?', 'Sulod lang.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Sa tinuod..." eignet sich gut, um frei in eine Aussage hineinzukommen.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 3,
|
||||
title: 'Freie Aussage bauen',
|
||||
instruction: 'Ordne die Wörter zu einer typischen Einleitung für eine freie Alltagsaussage.',
|
||||
questionData: {
|
||||
type: 'sentence_building',
|
||||
question: 'Baue: "Meistens..."',
|
||||
tokens: ['Kasagaran']
|
||||
},
|
||||
answerData: {
|
||||
correct: ['Kasagaran...']
|
||||
},
|
||||
explanation: 'Diese Einleitungen helfen, im freien Sprechen in Gang zu kommen.'
|
||||
},
|
||||
{
|
||||
title: 'Alltag frei erzählen',
|
||||
instruction: 'Sprich ohne deutsche Stütze eine kurze freie Alltagsaussage.',
|
||||
exerciseTypeId: 8,
|
||||
questionData: {
|
||||
type: 'speaking_from_memory',
|
||||
question: 'Erzähle kurz frei über deinen Alltag und nutze mindestens zwei dieser Einleitungen: Sa tinuod, Kasagaran, Usahay, Apan.',
|
||||
expectedText: 'Sa tinuod... Kasagaran... Usahay... Apan...',
|
||||
keywords: ['tinuod', 'kasagaran', 'usahay', 'apan']
|
||||
},
|
||||
answerData: {
|
||||
type: 'speaking_from_memory'
|
||||
},
|
||||
explanation: 'Hier geht es nicht mehr um perfekte Vorgabe, sondern um flüssige eigene Produktion.'
|
||||
}
|
||||
],
|
||||
|
||||
'Langzeitreview - Intensiv I': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Frühe Muster reaktivieren',
|
||||
instruction: 'Wähle den Ausdruck, der sicher im Langzeitgedächtnis sitzen sollte.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Welches frühe Fürsorgemuster musst du sofort wiedererkennen?',
|
||||
options: ['Nikaon na ka?', 'Asa ang porma?', 'Naa moy appointment?', 'Mubayad ko.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: 'Das Langzeitreview holt sehr frühe Kernmuster bewusst wieder nach vorn.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Frühe Preisfrage ergänzen',
|
||||
instruction: 'Fülle die Lücke mit dem passenden frühen Kernwort.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: '{gap} ni?',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['Tagpila']
|
||||
},
|
||||
explanation: '"Tagpila ni?" gehört zu den wichtigsten frühen Alltagsfragen.'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Frühe Routinen bündeln',
|
||||
instruction: 'Reagiere mit zwei sehr frühen Kernmustern.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Begrüße jemanden kurz und frage dann, ob die Person schon gegessen hat.',
|
||||
keywords: ['kumusta', 'nikaon']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Kumusta ka? Nikaon na ka?',
|
||||
keywords: ['kumusta', 'nikaon']
|
||||
},
|
||||
explanation: 'Das reviewt ganz bewusst sehr frühe, sehr wichtige Sozialmuster.'
|
||||
})
|
||||
],
|
||||
|
||||
'Langzeitreview - Intensiv II': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Frühe und späte Themen mischen',
|
||||
instruction: 'Wähle den Ausdruck, der zum Reaktivieren späterer Alltagsfelder gehört.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Welcher Ausdruck gehört klar zu Schule, Gesundheit oder Erledigungen?',
|
||||
options: ['resibo', 'Kumusta ka?', 'Palangga taka.', 'Sulod lang.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"resibo" steht hier für spätere Erledigungs- und Alltagsblöcke.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Gesundheit reaktivieren',
|
||||
instruction: 'Fülle die Lücke mit dem passenden Wort aus dem Gesundheitsbereich.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: 'Adto ta sa {gap}.',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['doktor']
|
||||
},
|
||||
explanation: 'Auch späte Alltagsfelder sollen im Langzeitreview schnell wieder greifbar sein.'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Späte Themen bündeln',
|
||||
instruction: 'Reagiere mit zwei kurzen Sätzen.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Sage, dass ihr zum Arzt geht, und erwähne danach ein Dokument oder einen Beleg.',
|
||||
keywords: ['doktor', 'resibo']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Adto ta sa doktor. Naa ko resibo.',
|
||||
keywords: ['doktor', 'resibo']
|
||||
},
|
||||
explanation: 'Das Langzeitreview mischt bewusst entfernte Themenfelder in einer kurzen Reaktion.'
|
||||
})
|
||||
],
|
||||
|
||||
'Hilfe & Unterstützung': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Um Hilfe bitten',
|
||||
instruction: 'Wähle die passendste Bitte um Unterstützung.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Wie fragst du höflich, ob dir jemand helfen kann?',
|
||||
options: ['Pwede ka motabang?', 'Asa ang sakayan?', 'Naa moy appointment?', 'Sulod lang.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Pwede ka motabang?" ist eine direkte, natürliche Hilfsbitte.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Hilfe anbieten ergänzen',
|
||||
instruction: 'Fülle die Lücke mit der passenden Hilfsformel.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: '{gap} tika.',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['Tabangan']
|
||||
},
|
||||
explanation: '"Tabangan tika." bedeutet "Ich helfe dir."'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Hilfe erfragen und anbieten',
|
||||
instruction: 'Reagiere in zwei kurzen Sätzen.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Bitte erst um Hilfe und bedanke dich danach kurz für die Unterstützung.',
|
||||
keywords: ['tabang', 'salamat']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Pwede ka motabang? Salamat sa tabang.',
|
||||
keywords: ['tabang', 'salamat']
|
||||
},
|
||||
explanation: 'Die Lektion verbindet Bitte und soziale Reaktion zu einem natürlichen Miniablauf.'
|
||||
})
|
||||
],
|
||||
|
||||
'Höflich reagieren und ablehnen': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Sanft ablehnen',
|
||||
instruction: 'Wähle die höflichste weiche Absage.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Wie lehnst du etwas freundlich und nicht zu direkt ab?',
|
||||
options: ['Dili lang sa karon.', 'Tabang!', 'Nikaon na ka?', 'Mubayad ko.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Dili lang sa karon." klingt deutlich weicher als ein hartes Nein.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Später statt jetzt',
|
||||
instruction: 'Fülle die Lücke mit der passenden weichen Reaktion.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: '{gap} na lang.',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['Sunod']
|
||||
},
|
||||
explanation: '"Sunod na lang." verschiebt höflich auf später.'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Höflich verschieben',
|
||||
instruction: 'Reagiere freundlich und weich.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Lehne eine Einladung höflich für heute ab und verschiebe sie auf später.',
|
||||
keywords: ['dili', 'sunod']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Dili lang sa karon. Sunod na lang.',
|
||||
keywords: ['dili', 'sunod']
|
||||
},
|
||||
explanation: 'Das ist genau die weiche soziale Reaktionsform dieser Lektion.'
|
||||
})
|
||||
],
|
||||
|
||||
'Feste & Einladungen': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Zur Feier einladen',
|
||||
instruction: 'Wähle die passende Einladungsformel.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Wie sagst du: "Ich lade dich ein"?',
|
||||
options: ['Giinvite tika.', 'Adto ta sa doktor.', 'Kapoy na ka?', 'Asa imong bag?']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Giinvite tika." ist eine alltagsnahe Einladungsform.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Zur Fiesta fragen',
|
||||
instruction: 'Fülle die Lücke mit dem passenden Wort.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: 'Moadto ka sa {gap}?',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['pista']
|
||||
},
|
||||
explanation: '"pista" steht hier für Feier oder Fiesta.'
|
||||
},
|
||||
withTypeName('situational_response', {
|
||||
title: 'Einladung und Treffpunkt',
|
||||
instruction: 'Reagiere in zwei kurzen Sätzen.',
|
||||
questionData: {
|
||||
type: 'situational_response',
|
||||
question: 'Lade jemanden ein und sage, dass ihr euch dort trefft.',
|
||||
keywords: ['invite', 'didto']
|
||||
},
|
||||
answerData: {
|
||||
modelAnswer: 'Giinvite tika. Magkita ta didto.',
|
||||
keywords: ['invite', 'didto']
|
||||
},
|
||||
explanation: 'Die Lektion verbindet Einladung und Verabredung in einer natürlichen Sozialszene.'
|
||||
})
|
||||
],
|
||||
|
||||
'Freies Erzählen - Mein Alltag': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Tagesablauf einleiten',
|
||||
instruction: 'Wähle die passendste Einleitung für einen Tagesablauf.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Welche Formulierung passt gut als Start in einen erzählten Tagesablauf?',
|
||||
options: ['Sa buntag...', 'Pila ni tanan?', 'Asa ang porma?', 'Sulod lang.']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Sa buntag..." eröffnet natürlich einen erzählten Tagesabschnitt.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Tagesabschnitt ergänzen',
|
||||
instruction: 'Fülle die Lücke mit dem passenden Zeitabschnitt.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: 'Sa {gap}...',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['hapon']
|
||||
},
|
||||
explanation: '"Sa hapon..." ist eine häufige Einleitung für den Nachmittag.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 8,
|
||||
title: 'Eigenen Alltag erzählen',
|
||||
instruction: 'Sprich frei über deinen Tagesablauf.',
|
||||
questionData: {
|
||||
type: 'speaking_from_memory',
|
||||
question: 'Erzähle kurz, was du morgens, nachmittags und abends machst.',
|
||||
expectedText: 'Sa buntag... Sa hapon... Sa gabii...',
|
||||
keywords: ['buntag', 'hapon', 'gabii']
|
||||
},
|
||||
answerData: {
|
||||
type: 'speaking_from_memory'
|
||||
},
|
||||
explanation: 'Hier steht die freie, zusammenhängende Produktion im Vordergrund.'
|
||||
}
|
||||
],
|
||||
|
||||
'Freies Erzählen - Familie, Sorgen, Pläne': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Sorge ausdrücken',
|
||||
instruction: 'Wähle die Formulierung, mit der du eine leichte Sorge ausdrückst.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Wie sagst du natürlich: "Ich bin etwas besorgt"?',
|
||||
options: ['Naguol ko gamay.', 'Mubayad ko.', 'Sulod lang.', 'Tagpila ni?']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"Naguol ko gamay." drückt eine leichte Sorge oder Niedergeschlagenheit aus.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 1,
|
||||
title: 'Plan ergänzen',
|
||||
instruction: 'Fülle die Lücke mit dem passenden Planungswort.',
|
||||
questionData: {
|
||||
type: 'gap_fill',
|
||||
text: 'Aduna koy {gap} unya.',
|
||||
gaps: 1
|
||||
},
|
||||
answerData: {
|
||||
type: 'gap_fill',
|
||||
answers: ['plano']
|
||||
},
|
||||
explanation: '"plano" ist hier das Schlüsselwort für einen bevorstehenden Plan.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 8,
|
||||
title: 'Familie, Sorge und Plan frei verbinden',
|
||||
instruction: 'Sprich frei in mehreren kurzen Sätzen.',
|
||||
questionData: {
|
||||
type: 'speaking_from_memory',
|
||||
question: 'Erzähle kurz von Familie, einer Sorge und einem Plan für später.',
|
||||
expectedText: 'Naguol ko gamay. Pero okay ra. Aduna koy plano unya.',
|
||||
keywords: ['naguol', 'okay', 'plano']
|
||||
},
|
||||
answerData: {
|
||||
type: 'speaking_from_memory'
|
||||
},
|
||||
explanation: 'Diese Lektion trainiert freie Verbindung von Gefühl, Familie und Planung.'
|
||||
}
|
||||
],
|
||||
|
||||
'Kultur, Familie & Sprache langfristig': [
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Kulturellen Kernbegriff erkennen',
|
||||
instruction: 'Wähle den Ausdruck, der stark mit respektvollem Umgang verbunden ist.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Welcher Ausdruck gehört besonders zum kulturellen Schwerpunkt von Respekt und Rücksicht?',
|
||||
options: ['respeto', 'plite', 'resibo', 'assignment']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"respeto" steht direkt für Respekt im sozialen Umgang.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 2,
|
||||
title: 'Familienkultur und Sprache',
|
||||
instruction: 'Wähle den Ausdruck, der besonders mit sozialem Miteinander verbunden ist.',
|
||||
questionData: {
|
||||
type: 'multiple_choice',
|
||||
question: 'Welcher Begriff verweist besonders auf gemeinschaftliches Mitziehen und gutes Miteinander?',
|
||||
options: ['pakikisama', 'doktor', 'ATM', 'sukli']
|
||||
},
|
||||
answerData: { type: 'multiple_choice', correctAnswer: 0 },
|
||||
explanation: '"pakikisama" ist ein zentraler kultureller Begriff für harmonisches Miteinander.'
|
||||
},
|
||||
{
|
||||
exerciseTypeId: 8,
|
||||
title: 'Kulturelle Schlüsselwörter laut festigen',
|
||||
instruction: 'Sprich die kulturellen Schlüsselwörter laut und bewusst.',
|
||||
questionData: {
|
||||
type: 'speaking_from_memory',
|
||||
question: 'Sprich die Wörter respeto, pakikisama, amping und palihug laut und deutlich.',
|
||||
expectedText: 'respeto pakikisama amping palihug',
|
||||
keywords: ['respeto', 'pakikisama', 'amping', 'palihug']
|
||||
},
|
||||
answerData: {
|
||||
type: 'speaking_from_memory'
|
||||
},
|
||||
explanation: 'Die Schlusslektion verankert kulturelle Schlüsselwörter bewusst als Langzeitmarker.'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
178
docs/FALUKANT_PREGNANCY_SCHEDULED_BIRTH_DAEMON_SPEC.md
Normal file
178
docs/FALUKANT_PREGNANCY_SCHEDULED_BIRTH_DAEMON_SPEC.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# Falukant: Geplante Schwangerschaft & Geburt – Daemon- und Datenbankkonzept
|
||||
|
||||
Dieses Dokument beschreibt das **vereinheitlichte Konzept** für Schwangerschaft, die der **externe Daemon** (`UserCharacterWorker`, C++) verarbeiten soll, sowie die **Datenbank** und die **Abgrenzung zur bestehenden Zufallslogik**. Ziel ist, dass gesetzte Termine (`pregnancy_due_at`) und der gewählte Vater (`pregnancy_father_character_id`) – u. a. aus dem Admin-Tool – **tatsächlich zu Geburten führen**, ohne dass nur die Node-API „Geburt erzwingen“ funktioniert.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ist-Zustand (Problem)
|
||||
|
||||
### 1.1 Datenbank (bereits vorhanden)
|
||||
|
||||
Auf `falukant_data."character"` existieren (siehe `backend/sql/add_character_pregnancy.sql`):
|
||||
|
||||
| Spalte | Typ | Bedeutung |
|
||||
|--------|-----|-----------|
|
||||
| `pregnancy_due_at` | `TIMESTAMPTZ` NULL | Erwarteter Geburtstermin |
|
||||
| `pregnancy_father_character_id` | `INTEGER` NULL, FK auf `character(id)` | Vater-Charakter |
|
||||
|
||||
Die **Node-Backend**-Logik (Admin, `falukantService`) **liest und schreibt** diese Felder.
|
||||
|
||||
### 1.2 Daemon – aktuelles Verhalten
|
||||
|
||||
In `src/usercharacterworker.cpp` / `src/usercharacterworker.h`:
|
||||
|
||||
- `processPregnancies()` führt **`QUERY_GET_PREGNANCY_CANDIDATES`** aus.
|
||||
- Diese Query:
|
||||
- bezieht sich **nur** auf Ehen (`falukant_type.relationship.tr = 'married'`),
|
||||
- nutzt **keine** `pregnancy_*`-Spalten,
|
||||
- modelliert Geburten **per Zufallsprozess** (altersabhängige Wahrscheinlichkeit),
|
||||
- setzt implizit **`character1_id` = Vater** und **`character2_id` = Mutter** (fest verdrahtet, ohne Geschlechts- oder Rollenprüfung).
|
||||
|
||||
**Folge:** Wer im Spiel oder per Admin als „schwanger“ mit Termin und Vater gespeichert wird, **wird vom Daemon nicht erkannt**. Geburten aus diesem Konzept passieren nur, wenn sie **manuell** per Node (z. B. Admin „Geburt erzwingen“) ausgelöst werden.
|
||||
|
||||
**Zusätzlich:** `QUERY_AUTOBATISM` setzt nur `child_relation.name_set` nach einigen Tagen – hat **nichts** mit Schwangerschafts-Spalten zu tun.
|
||||
|
||||
---
|
||||
|
||||
## 2. Soll-Konzept (Zwei parallele Wege, eine klare Priorität)
|
||||
|
||||
### 2.1 Weg A – **Geplante Schwangerschaft** (DB-gesteuert, neu für den Daemon)
|
||||
|
||||
**Auslöser:** `pregnancy_due_at` ist gesetzt und der Zeitpunkt ist **fällig**.
|
||||
|
||||
**Regeln (Vorschlag):**
|
||||
|
||||
1. **Eine** Mutter ist immer der Datensatz in `character`, auf dem `pregnancy_due_at` und die Schwangerschaft „liegen“.
|
||||
2. **Vater:** `pregnancy_father_character_id`
|
||||
- Wenn gesetzt: muss ein existierender Charakter sein, **nicht** `mother.id`.
|
||||
- Wenn `NULL`: Policy festlegen (siehe Abschnitt 8).
|
||||
3. **Fälligkeit:** z. B. `date_trunc('day', pregnancy_due_at AT TIME ZONE 'Europe/Berlin') <= current_date` (oder einheitlich **UTC** – wichtig ist **eine** Definition im Team).
|
||||
4. Nach erfolgreicher Geburt: **`pregnancy_due_at` und `pregnancy_father_character_id` auf NULL** setzen (Schwangerschaft beendet).
|
||||
|
||||
Dieser Weg entspricht dem, was Admin und Spieler erwarten, wenn ein **Termin** existiert.
|
||||
|
||||
### 2.2 Weg B – **Legacy: Zufallsgeburten bei Ehe** (bestehende Query)
|
||||
|
||||
Die Query `QUERY_GET_PREGNANCY_CANDIDATES` modelliert **„natürliche“** Zufallsgeburten ohne `pregnancy_*`-Felder.
|
||||
|
||||
**Entscheidung für die Umsetzung:**
|
||||
|
||||
- **Option B1:** Weg B **abschalten** oder stark reduzieren, wenn Weg A das offizielle Modell ist.
|
||||
- **Option B2:** Weg B **beibehalten** für Atmosphäre, aber **nur**, wenn **keine** aktive geplante Schwangerschaft auf den betroffenen Charakteren existiert (doppelte Geburten vermeiden).
|
||||
- **Option B3:** Weg B nur noch **Simulation**, bis Spiel-Logik „Konzeption“ explizit setzt (größerer Umbau).
|
||||
|
||||
**Empfehlung:** Mindestens **B2** oder **B1**, damit keine zwei Geburten pro Tag für dieselbe Ehe aus unterschiedlichen Regeln entstehen.
|
||||
|
||||
---
|
||||
|
||||
## 3. Datenbank – Erweiterungen (Vorschlag)
|
||||
|
||||
Die Minimalvariante kommt **ohne** neue Spalten aus (nur Daemon-Logik). Für Robustheit und spätere Features sind **optionale** Erweiterungen sinnvoll:
|
||||
|
||||
### 3.1 Pflicht (kein Schema-Zwang, aber empfohlen)
|
||||
|
||||
- **Index** für den Daemon-Tick, z. B.:
|
||||
|
||||
```sql
|
||||
CREATE INDEX IF NOT EXISTS idx_character_pregnancy_due
|
||||
ON falukant_data."character" (pregnancy_due_at)
|
||||
WHERE pregnancy_due_at IS NOT NULL;
|
||||
```
|
||||
|
||||
### 3.2 Optional – Metadaten
|
||||
|
||||
| Spalte | Typ | Zweck |
|
||||
|--------|-----|--------|
|
||||
| `pregnancy_source` | `TEXT` oder `ENUM` | z. B. `admin`, `gameplay`, `npc` – für Logging und Regeln |
|
||||
| `pregnancy_conception_at` | `TIMESTAMPTZ` | optional, für Anzeige/Quests |
|
||||
| `pregnancy_birth_context` | `TEXT` | `marriage` | `lover` – für korrekten `child_relation.birth_context` ohne Heuristik |
|
||||
|
||||
Wenn `pregnancy_birth_context` **nicht** eingeführt wird, leitet der Daemon den Kontext aus der **Beziehung** zwischen Mutter und Vater ab (verheiratet → `marriage`, Liebhaber → `lover`, sonst Default `marriage` oder Policy).
|
||||
|
||||
### 3.3 `child_relation` – Parität mit Node
|
||||
|
||||
Das Sequelize-Modell erwartet u. a. `father_name`, `mother_name`, `legitimacy`, `birth_context`, `public_known`. Die **Daemon-INSERT**-Query (`QUERY_INSERT_CHILD_RELATION`) muss mit der **realen DB** übereinstimmen (Pflichtfelder, Defaults). Falls nötig:
|
||||
|
||||
- INSERT um **Namen** (aus Vordefiniert-Tabellen) und **legitimacy / birth_context / public_known** erweitern,
|
||||
- oder DB-Defaults / Trigger ergänzen,
|
||||
- **gleiche Semantik** wie `adminForceFalukantBirth` in `adminService.js` anstreben.
|
||||
|
||||
---
|
||||
|
||||
## 4. Daemon – Umsetzung (technisch)
|
||||
|
||||
### 4.1 Neue SQL-Query: „fällige geplante Geburten“
|
||||
|
||||
**Skizze (logisch):**
|
||||
|
||||
- SELECT Mutter-`character` `c` mit:
|
||||
- `c.pregnancy_due_at IS NOT NULL`
|
||||
- `c.pregnancy_due_at` fällig (siehe Zeitzone)
|
||||
- JOIN Vater `c_father` auf `c.pregnancy_father_character_id = c_father.id` **wenn** Vater Pflicht ist
|
||||
- Pro Zeile: `father_cid`, `mother_cid`, `region_id`, `title_of_nobility` (vom passenden Elternteil), `last_name`, `father_uid`, `mother_uid` analog zur bestehenden Schleife.
|
||||
|
||||
**Wichtig:** Vater/Mutter **nicht** aus `relationship.character1/2` ableiten, sondern aus **expliziten IDs** (`pregnancy_father_character_id` + Zeilen-ID der Mutter).
|
||||
|
||||
### 4.2 Ablauf in `processPregnancies()`
|
||||
|
||||
1. `QUERY_AUTOBATISM` wie bisher (optional, Reihenfolge beachten).
|
||||
2. **Neu:** Transaktion oder feste Reihenfolge:
|
||||
- Kandidaten für **geplante Geburt** laden
|
||||
- **pro Kandidat:** Kind einfügen (`QUERY_INSERT_CHILD` oder gemeinsame Funktion), `child_relation` einfügen, **Schwangerschaft** auf der Mutter **leeren**
|
||||
- Benachrichtigungen (`children_update`, `falukantUpdateStatus`) wie bei bestehender Schleife
|
||||
3. **Legacy-Query** nur ausführen, wenn nach Absprache (Option B1–B3).
|
||||
|
||||
**Idempotenz:** Pro Tick darf dieselbe schwangere Zeile **nicht** zweimal gebären. Am sichersten: **UPDATE … RETURNING** oder **DELETE** der Schwangerschaftsdaten in derselben Transaktion wie das Kind.
|
||||
|
||||
### 4.3 Geschlecht der Eltern
|
||||
|
||||
Optional: Plausibilitätsprüfung (`gender` Mutter/Vater) – kann im ersten Schritt weggelassen werden, sollte aber langfristig mit dem Spielregelwerk übereinstimmen.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend (Node) – Abstimmung
|
||||
|
||||
- **Zeitzonen:** `pregnancy_due_at` wird in JS als `Date` gesetzt; Daemon muss **dieselbe** Fälligkeitsdefinition nutzen wie die UI („heute“ = 0 Tage).
|
||||
- **WebSocket:** Nach Daemon-Geburt dieselben Events wie bei Admin-Geburt, damit Clients die Familie aktualisieren.
|
||||
|
||||
---
|
||||
|
||||
## 6. Frontend
|
||||
|
||||
- Hinweis in der Familien-Ansicht: „Geburt erfolgt automatisch am Termin“ (wenn Daemon aktiv), sonst „nur nach Admin-Aktion“ – je nach Rollout.
|
||||
|
||||
---
|
||||
|
||||
## 7. Test-Checkliste
|
||||
|
||||
- [ ] Admin: Schwangerschaft mit Termin **heute** und Vater – nach Daemon-Lauf: Kind existiert, Schwangerschaft weg.
|
||||
- [ ] Kein Vater (`NULL`) – definiertes Verhalten (Fehler loggen / überspringen / NPC-Vater – **Policy**).
|
||||
- [ ] Doppel-Tick: keine doppelte Geburt.
|
||||
- [ ] Legacy-Zufallsgeburt: keine Kollision mit aktivem `pregnancy_due_at` auf derselben Mutter.
|
||||
- [ ] `child_relation` und `character`-Kind konsistent mit Node-Admin-Geburt.
|
||||
|
||||
---
|
||||
|
||||
## 8. Offene Policy-Fragen (bitte vor Implementierung festlegen)
|
||||
|
||||
1. **Vater `NULL`:** Geburt abbrechen, stillen Vater aus Beziehung erraten, oder festen Platzhalter?
|
||||
2. **Legacy-Query:** komplett entfernen oder nur noch unter Bedingungen?
|
||||
3. **Zeitzone** für „Termin ist heute“: Server-UTC, Europe/Berlin, oder Nutzer-TZ?
|
||||
4. **Liebschaftsgeburten:** nur über `pregnancy_*` + `birth_context`/`pregnancy_birth_context`, nie über reine Ehe-Query?
|
||||
|
||||
---
|
||||
|
||||
## 9. Referenzdateien im Repo
|
||||
|
||||
| Bereich | Datei |
|
||||
|---------|--------|
|
||||
| Daemon | `src/usercharacterworker.cpp` (`processPregnancies`) |
|
||||
| SQL-Strings | `src/usercharacterworker.h` (`QUERY_GET_PREGNANCY_CANDIDATES`, `QUERY_INSERT_CHILD`, `QUERY_INSERT_CHILD_RELATION`) |
|
||||
| DB-Spalten Schwangerschaft | `backend/sql/add_character_pregnancy.sql` |
|
||||
| Modell Character | `backend/models/falukant/data/character.js` |
|
||||
| Admin / Geburt Node | `backend/services/adminService.js` (`adminForceFalukantPregnancy`, `adminForceFalukantBirth`) |
|
||||
| Familie API | `backend/services/falukantService.js` (`_getCharacterPregnancyOptional`, `getFamily`) |
|
||||
|
||||
---
|
||||
|
||||
*Stand: technische Analyse des Repos; zur Abstimmung mit Game-Design und Deployment des Daemons.*
|
||||
Reference in New Issue
Block a user