Schedim

Hallo, bitte anmelden!


Letzte Änderungen

Kampagnen-PDF: Layout-Fehler, die erst der Ausdruck gezeigt hat 13.09.2026 · Ingo Wiarda · e4770f5
Drei Sachen, die in den Testtexten richtig aussahen und auf dem Blatt falsch: - Bild und Unterschrift gehörten nicht zusammen. Als zwei Elemente hinzugefügt sind sie zwei Dinge, die einzeln platziert werden: das zweite Bild einer Szene passte nicht mehr, floss auf die nächste Seite und ließ seine Unterschrift unter der ersten zurück. Beides steckt jetzt in einer Zelle mit setKeepTogether. - Weil das Bild erst beim nächsten Seitenanfang platziert wurde, landete es auf der Anhangsseite - über der Loot-Tabelle, unter einer Fußzeile ohne Szenenname. Mit dem Block zusammen passiert der Umbruch dort, wo die Szene gezeichnet wird, und die Seite trägt wieder ihren Namen. - Der Statblock eines Mobs war über zwei Seiten zerrissen (Kopf auf der einen, Tabellen auf der anderen) und der Name stand doppelt da: einmal als eigene Überschrift, einmal aus dem Charakterbogen selbst. Jetzt eine Seite je Mob und keine eigene Überschrift mehr. Die vier Tests, die dabei rot wurden, waren wieder an Seitenindizes gebunden - dieselbe Falle wie bei der Fußzeile. Sie fragen jetzt das Dokument statt eine Seitennummer: "welche Seite enthält den Statblock" statt "die letzte Seite". Gefunden, indem die Seiten als PNG gerendert und angesehen wurden. Kein Textauszug hätte das gezeigt: die Unterschrift stand ja da, nur eben ohne Bild. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Kampagnen-PDF: Lesezeichen je Szene und für den Anhang 12.09.2026 · Ingo Wiarda · 73b2777
Letztes Inkrement aus docs/plans/kampagnen-pdf.adoc. Die Seitenleiste jedes Readers listet jetzt die Szenen in Reihenfolge und danach den Anhang; ohne Anhang gibt es auch keinen Eintrag dafür. Angelegt wird ein Eintrag direkt hinter dem Umbruch, der seine Seite beginnt - das Ziel ist die gerade aktuelle Seite. Kein Inhaltsverzeichnis mit Seitenzahlen: die wären erst bekannt, wenn die Seiten existieren. Auf Papier macht die Fußzeile die Arbeit, im Reader die Seitenleiste. Der Test prüft die Ziele mit und nicht nur die Titel - ein Lesezeichen, das auf Seite 1 zeigt, käme durch eine Titelprüfung durch. Geprüft wird "das erste zeigt auf Seite 2" und "jedes weitere zeigt tiefer hinein", nicht feste Seitenzahlen: dieselbe Lehre wie bei der Fußzeile, wo feste Indizes zweimal das Falsche behauptet haben. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Fix: Fußzeile merkt die Szene beim Seitenbeginn, nicht beim Seitenende 12.09.2026 · Ingo Wiarda · 2a61eff
Zweiter Versuch an derselben Stelle, und diesmal ohne Verlass auf den Zeitpunkt. Der erste Fix verschob nur das Löschen des Etiketts; auf play blieb damit die Folgeseite einer Szene ohne Namen. Der Grund: wann openpdf eine Fußzeile tatsächlich schreibt, ist nicht dasselbe für eine Seite, die von selbst voll wurde, und eine, die ein ausdrücklicher Umbruch beendet. Ein Feld, das beim Seitenende gelesen wird, liefert deshalb für einen Teil der Seiten die richtige Antwort und für den Rest die nächste Szene oder keine. Jetzt wird beim Seitenbeginn (onStartPage) festgehalten, zu welcher Szene die Seite gehört, und beim Seitenende nur nachgesehen. Der Seitenbeginn ist eindeutig: welche Szene gerade gezeichnet wird, besitzt die Seite. Der Test prüft nicht mehr Seitenindizes, sondern die Folge der Etiketten über das ganze Dokument - nichts auf der Titelseite, dann je Szene ein zusammenhängender Lauf in Reihenfolge, dann nichts mehr. Die Indizes waren der Grund, warum die ersten zwei Versuche grün waren: eine Szene mit Bildern läuft über, und ein Anhang mit Statblock ebenfalls, also weiß dieser Test die Seitenzahl nicht mehr. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Fix: die letzte Szene verlor ihren Namen in der Fußzeile 12.09.2026 · Ingo Wiarda · 23359f7
Auf play sichtbar geworden: eine Kampagne mit einer einzigen Szene hatte auf keiner Seite den Szenennamen in der Fußzeile, obwohl der Test "die Folgeseite nennt die Szene" grün war. Die Fußzeile einer Seite wird geschrieben, wenn die Seite *endet* - für die letzte Szene also erst im Seitenumbruch des Anhangs (oder in close()). setScene(null) stand vor diesem Umbruch und hat das Etikett genau dieser Seite gelöscht. Bei einer Kampagne mit einer Szene ist das jede Szenenseite. Das Löschen steht jetzt hinter dem Umbruch, im Anhang selbst. Der alte Test prüfte nur eine Folgeseite der *ersten* Szene und war deshalb grün. Der neue prüft jede Szenenseite, ausdrücklich auch die letzte, und dass Titelseite und Anhang keinen Szenennamen tragen. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Kampagnen-PDF: Bilder der Szenen, und die Fußzeile nennt die Szene 12.09.2026 · Ingo Wiarda · 3bf43ec
Inkrement 5 aus docs/plans/kampagnen-pdf.adoc. Bilder werden eingebettet, mit Namen als Unterschrift und, wo die Spieler sie noch nicht gesehen haben, mit "Erst auf Zuruf". Nicht die gespeicherte Vorschau: die ist 256 px auf der langen Seite, also etwa 43 dpi auf 150 mm Druckbreite - klotzig auf einem Blatt, das man ausdruckt und mitnimmt. Genommen wird das Original, beim Rendern auf 1400 px verkleinert (rund 240 dpi) und nur, wenn es überhaupt größer ist; Verkleinertes als PNG bei Alphakanal, sonst als JPEG, weil ein Foto als PNG neu kodiert größer wird als das Original. Dekodiert und skaliert wird einmal, vor beiden Zeichendurchläufen - die Seitenzählung würde sonst die teure Hälfte doppelt machen. Das war beim Zwei-Durchlauf-Entscheid schon vorgemerkt. Der Test zählt Bild-XObjects je Seite, statt nach Unterschriften zu suchen: eine Unterschrift ist Text und würde auch ohne Bild gedruckt. Das hat dann auch etwas gefunden - eine Szene mit Bildern passt nicht mehr auf eine Seite, das zweite Bild landet auf einer Folgeseite. Deshalb nennt die Fußzeile jetzt die Szene: sonst trägt so ein Blatt ein Bild und eine Tabelle unter keiner Überschrift und ist auf einem Stapel Ausdrucke nicht zuzuordnen. Außerdem im Plan und am Modell festgehalten, wofür das Ganze da ist: der Spielleiter baut hier und nimmt das Dokument an einen echten Tisch mit. Der Handout-Export ist eine andere Sache und ist zurückgestellt, bis es die Markierung gibt, auf der er beruhen müsste - revealed heißt "im Raum gezeigt", nicht "dies ist ein Handout". Audience.HANDOUT bleibt als getestetes Gerüst stehen, ohne Endpunkt. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Kampagnen-PDF: Mobs und Loot je Szene, Statblöcke im Anhang 12.09.2026 · Ingo Wiarda · a11c034
Inkrement 3 aus docs/plans/kampagnen-pdf.adoc. Je Szene eine Mob-Tabelle ("Brückentroll ×3") und eine Loot-Tabelle mit Anzahl, Sichtbarkeit und Träger. Die Sichtbarkeit steht in den Worten, die der Editor für die beiden Zustände benutzt - "liegt sichtbar da" gegen "muss gefunden werden" - statt als nacktes Flag. Dahinter der Anhang: die Loot-Liste der Kampagne und je Mob ein Statblock. Im Anhang und nicht in der Szene, weil ein Mob in mehreren Szenen auftritt und überall dieselben Werte hat; die Szene sagt ×3, der Block steht einmal. Der Statblock ist der des Charakterbogens. Dafür ist nicht jede Tabelle freigelegt, sondern eine Naht: CreaturePdfService.appendSheet(document, creature) hängt die Blöcke in ein Dokument, das jemand anders geöffnet hat, und generate() benutzt jetzt dieselbe Methode. So ist ein Statblock einmal definiert; sechs einzelne Bauer hätten jedem Aufrufer erlaubt, eine andere Auswahl zu treffen. Dass der Bogen selbst unverändert aussieht, prüfen die fünf Tests, die es dafür schon gab. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Kampagnen-PDF: Grundgerüst und Spielleiter-Endpunkt 12.09.2026 · Ingo Wiarda · 682e1d0
Inkrement 2 aus docs/plans/kampagnen-pdf.adoc. CampaignPdfService zeichnet ein CampaignDocument: Titelseite mit Name, Spielsystem, Fassung und Beschreibung, dann je Szene eine Seite mit Beschreibung, Spielleiter-Notiz im grauen Kasten und den Textblöcken - unenthüllte mit "Erst auf Zuruf" beschriftet, in den Worten, die der Editor für diesen Zustand benutzt. Im Renderer steht kein einziges if (handout). Was eine Zielgruppe sehen darf, hat CampaignDocumentService entschieden; hier wird nur gezeichnet. Genau deshalb existiert die Trennung. Die Seitenzahl ist anders gelöst als der Plan vorschlug: das Dokument wird zweimal gezeichnet, einmal zum Zählen, einmal mit der Gesamtzahl. Der klassische PdfTemplate-Trick spart den Durchlauf und kostet einen Schlüssel, der mitten im Satz endet ("Seite {0} von"), sowie einen Off-by-one in getPageNumber(). Ein Textdokument zweimal zu zeichnen ist billig; der Test prüft "Seite 1 von 3" bis "Seite 3 von 3", wo ein Off-by-one auffällt. GET /campaign/{id}/export/pdf in SceneController, mit ensureAuthor wie die beiden anderen Exporte, plus Knopf auf campaign/edit. Außerdem korrigiert: der Kommentar an collect() nannte "Beschreibungen bleiben im Handout" eine Ermessensfrage. Ist sie nicht - der Editor beschriftet das Feld als "Beschreibung für die Spieler" und die Notiz als "sieht nur der Spielleiter". Die Trennung folgt den Feldern. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
CampaignDocument: was in einem Kampagnen-Dokument steht, je Zielgruppe 12.09.2026 · Ingo Wiarda · ccfc695
Inkrement 1 aus docs/plans/kampagnen-pdf.adoc, die Leseseite der Kampagnen-Ausgabe. Noch kein PDF. CampaignDocument sind Records ohne Verhalten; Bilder tragen ids statt Bytes, weil die Originale den Großteil eines Kampagnen-Archivs ausmachen und ein Modell, das sie hält, zum Wegwerfen gebaut wäre. CampaignDocumentService.collect entscheidet *einmal*, was eine Zielgruppe sehen darf, und benutzt dieselben Abfragen wie CampaignExportService - eine zweite Menge wäre eine zweite Antwort auf "was steht in dieser Kampagne, in welcher Reihenfolge". Das Handout verliert: jede Spielleiter-Notiz, jeden Textblock und jedes Bild ohne revealed, und Mobs und Loot vollständig - beide Pools und ihr Auftreten je Szene. Dass drei Trolle an der Brücke warten, ist Inhalt, nicht Formatierung. Szenenbeschreibungen bleiben drin. Das ist die eine Ermessensentscheidung, sie steht mit Begründung am Javadoc von collect. Audience heißt Audience und nicht Fassung: der Plan hatte deutsche Konstanten vorgeschlagen und damit gegen die eigene Regel aus CLAUDE.md verstoßen; der Plan ist mitkorrigiert. Der Sammler behält den Namens-Fallback von CampaignExportService, aber mit richtigem Kommentar: er ist nicht erreichbar. Die Pool-Abfrage joint creature inner, und deleteCreatureCascading löscht die Pool-Zeilen vor der Kreatur - ein Eintrag ohne Vorlage ist kein Zustand, den es gibt. Ein Test hält das fest. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Zauber auf dem Charakterbogen 12.09.2026 · Ingo Wiarda · 10dd4d2
Die dritte Angriffsquelle, die noch fehlte. Eigener Block statt einer Zeile in den Angriffen, weil zwei Spalten dazukommen, die Waffe und natürlicher Angriff nicht haben: Manakosten und die Stufe der Zauberfertigkeit, mit der skalierende Würfel multipliziert werden. Der Angriffswert kommt aus demselben Aufruf wie bei den anderen Quellen, mit AttackSource.spell. castableSpells ist bereits auf Kampfmagie eingegrenzt; sein affordable-Flag wird bewusst ignoriert, weil es gegen den aktuellen Manavorrat vergleicht - der ist während der Sitzung, für die man den Bogen druckt, ständig ein anderer. Ohne Zauber entfällt der Block ganz, dieselbe Entscheidung wie beim Mana im Kopf: leer stünde er sonst auf jedem Bogen jedes Systems ohne Magie. Ein Test prüft beides - die ganze Zeile für den Magier, die Abwesenheit beim Söldner. Die Würfel-Darstellung ist jetzt damageText() und damit für Waffen, natürliche Angriffe und Zauber dieselbe. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F
Angriffe je Quelle auf dem Charakterbogen 12.09.2026 · Ingo Wiarda · 354172f
Der aggregierte Angriffswert sagt für eine unbewaffnete Kreatur 0 und für eine bewaffnete nur den besten Beitrag - auf dem gedruckten Bogen stand damit "Angriff 0, Schaden 0" bei einer Kreatur mit Schwert und Biss. CreaturePdfService.attacksTable gibt jetzt je ausgerüsteter Waffe und je natürlichem Angriff eine Zeile: Angriffswert über combatValue(creature, ATTACK, source), Würfel über damageDice - dieselben Aufrufe in derselben Reihenfolge wie CharacterDerivedExportService, damit Bogen und Foundry-Export sich nicht über den Wert eines Schlages widersprechen. Zwei Feinheiten, die dort schon dokumentiert sind: natürliche Angriffe werden je Fertigkeit genommen, nicht je Schadenszeile (sonst erscheint ein Biss mit getypter und ungetypter Komponente zweimal, während der Server beide als einen Schlag würfelt), und findEquipped statt einer eigenen Filterung, damit "ausgerüstet" keine zweite Antwort bekommt. Zauber bleiben außen vor: sie brauchen Manakosten und eine eigene Spalte. Der Test prüft ganze Zeilen ("Langschwert -2 1W6", "Biss -3 1W4"), weil erst die unterschiedlichen Zahlen zeigen, dass je Quelle aufgelöst wird. Claude-Session: https://claude.ai/code/session_018UUooLMDSwYZMY3SCQyx8F

Ältere Änderungen anzeigen