Archiv der Kategorie: Computerspiele

Fraktale Blitze


Nachdem mein Texturgenerator auch Fraktale erzeugen konnte, lag es nahe, daraus eine Grafik für ein Spieleprojekt zu basteln. Blitzzauber hatten es mir immer schon angetan, und auch für technische Objekte machen sich Blitze immer wieder gut. Also machte ich mich an die Arbeit, um aus einem passenden Ausschnitt der Mandelbrotmenge einen Blitz zu machen:

Fraktaler Blitz
Fraktaler Blitz

Es fehlt zwar etwas am 3D-Eindruck, aber in einigen Montagen mit älteren Screenshots meines „Juwelensuche“-Projektes wirkt der Blitz doch recht schön:

Bildmontagen als Beispiele
Bildmontagen als Beispiele

Den Blitz zu animieren habe ich noch nicht versucht, aber ich denke, man kann über Skalierung und Spiegelung eine Sequenz basteln, die als Animation ausreichend gut wirkt. Das Auge ist sowieso zu träge, um die Details eines sekundenschnellen Blitzzaubers aufzulösen. D.h. die Animation muss vor allem den Eindruck „Es bewegt sich etwas“ bewirken, die Details wie es sich bewegt sind in dem Fall nicht ganz so wichtig.

Die Blitzgrafik in höherer Auflösung und einige Montagen habe ich als Creative Commons auf opengameart.org veröffentlicht:

https://opengameart.org/content/lightning-effect-0

Advertisements

Nahtlos wiederholbare Texturen aus 4D Rauschen


Unlängst schrieb ich noch, dass ich nicht weiß, wie man nahtlos wiederholbare Texturen aus Simplex Noise erzeugen kann. Versuche per Modulo-Operator zu wiederholbaren Mustern zu kommen schlugen fehl. Ich denke trotzdem, dass es per Modulo gehen müsste, aber mir fehlt das Verständnis für die „Innereien“ der Simplex Noise, um den Eingriff richtig durchzuführen.

Bei einer Recherche nach hilfreichen Informationen bin ich dann auf diesen Artikel gestoßen:

https://www.gamedev.net/blogs/entry/2138456-seamless-noise/

Die Idee mit dem 3D Torus hatte ich auch schon, aber aus eben dem Grund verworfen, den der Autor des Artikels auch nennt – es gibt Verzerrungen im Muster, weil der innere Radius des Torus kleiner ist als der äußere und die abgerollte Fläche streng genommen kein Rechteck ist, bzw. wenn man sie auf ein Rechteck abbildet, dann wird das Muster auf der Fläche verzerrt.

Was ich nicht wusste, ist, dass man den Torus-Trick auch in vier Dimensionen machen kann, und dass sich die Einbettung zweier Kreisbewegungen in vier Dimensionen verzerrungsfrei auf ein Rechteck abbilden lässt.

Nahtlos wiederholbares Muster
Nahtlos wiederholbares Muster

Der Nachteil ist, dass die Berechnung der Simplex Noise in vier Dimensionen deutlich langsamer ist als in zwei Dimensionen. Aber zumindest habe ich jetzt eine Möglichkeit, nahtlos wiederholbare Texturen zu errechnen.

Wiederholung 2x2
Wiederholung 2×2

Da fragen die Leute immer, wozu im Leben man so viel Mathe braucht – also wenn man mit prozeduraler  Computergrafik arbeitet, dann beantwortet sich die Frage von selbst. Und selbst wenn ich viel Mathe im Studium gelernt habe, es war immer noch nicht genug für diese Art von Anwendungen. Sonst könnte ich jetzt die Simplex Noise auch Modulo rechnen und müsste nicht über den 4D Trick gehen, der sich mir auch nur ungefähr erschliesst – auch wenn ich die Abwicklung eines 3D Torus auf eine 2D Fläche mathematisch gerade noch so hinbekommen hätte, in 4D hätte ich das ohne die Hilfe des Artikels nicht geschafft, besser gesagt, ich hätte mir nicht mal vorstellen können, dass es für 4D eine analoge und verzerrungsfreie Abwicklung gibt.

 

 

Landschaftsgenerator zu Texturgenerator


Schon beim Entwurf der ersten Fassung meines Landschaftsgenerators schwang die Idee mit, nicht nur Höhenprofile sondern auch andere Sorten von Texturen zu erzeugen. Naheliegend war, die Simplex-Noise Funktion auch für die klassischen Wolkenstrukturen zu verwenden:

Wolkentextur
Wolkentextur

Weitere Ideen sind Gesteinssorten wie Granit oder Marmor, die sich per Simplex-Noise ebenfalls relativ leicht erzeugen lassen. Dazu kämen noch Holz und Zwiebelstrukturen.

Unverhältnismässig aufwändig war die Programmierung des Farbgradienten-Einstellers. Aber das ist jetzt geschafft, und man kann Farbgradienten als lineare Interpolation durch eine beliebige Anzahl von Stützpunkten festlegen. Schöner wäre vermutlich eine kubische Interpolation, aber im Moment bin ich froh, dass es überhaupt funktioniert.

Was ich noch nicht herausgefunden habe ist es, wie man Simplex-Noise modulo der Texturgröße berechnen kann, um Muster zu erzeugen, deren linke Seite nahtlos and die rechte passt, ebenso die obere an die untere Seite, damit man nahtlose Kachelmuster erstellen kann.

Landschaftsgenerator


Letzte Woche war ich auf der Suche nach Landschaftsgeneratoren. Irgendwie konnte ich jedoch nichts finden, was mir gefallen hätte und gleichzeitig kostenlos war. Aber dafür fand ich diesen Artikel hier:

http://ranmantaru.com/blog/2011/10/08/water-erosion-on-heightmap-terrain/

Erosion von Landschaften war etwas, das mich schon länger interessiert hat, aber ich war zu faul, so einen Algorithmus selbst zu kodieren. Der Beispielcode dort war C, schien mir aber einfach genug um ihn „schnell“ nach Java zu portieren. Ein paar Stunden hat es dann doch gedauert … und noch sind nicht alle Parameter über das UI editierbar. Aber es reicht für erste Experimente.

Terraingenerator v0.01
Terraingenerator v0.01

Das schaut jetzt noch nicht so grandios aus, aber in 3D sieht man die Effekte der Erosion sehr schön.

Terrain in 3D
Terrain in 3D

Der Algorithmus füllt die Senken auf sehr flache Weise, bislang habe ich es noch nicht geschafft, Parameter zu finden, die mehr rundlich gefüllte Senken ergeben würden und gleichzeitig die Bergkuppen weitgehend intakt lassen. Aber bis auf dieses Detail werden die Hänge gut abgetragen, und es bilden sich schöne Hochtäler und Plateaus.

Hier ein zweites Beispiel mit einem anderen Satz Parameter. Steilere Berge, und eine insgesamt welligere Basislandschaft.

Terrain in 3D
Terrain in 3D

Und zuletzt noch eine Variante mit 6 statt 3 Oktaven der Noise-Funktion, d.h. mehr kleinräumige Details in der Landschaft

Terrain in 3D, 6 Oktaven
Terrain in 3D, 6 Oktaven

Das sieht jetzt sehr nach den Fotos der Mars-Rover aus – ich vermute mal, das ist ein gutes Zeichen und zeigt, dass die Kombination der Noise-Funktion und des Erosions-Algortithmus mit diesem Satz Parameter schon recht nahe an der Natur ist.

Was fehlt sind Flüsse. Im Prinzip kann man den Algorithmus auch für Flüsse verwenden, da so ein „Droplet“ den Fluss von Wasser über die Landschaft simuliert. Flüsse transportieren das Geröll jedoch weiter, und, da sie stationär sind, schneiden sie sich tiefer in die Landschaft ein.  D.h. es wird ein zweiter Durchgang notwendig, mit einigen Stationären Droplet-Quellen und anderen Parametern, aber ich bin schon sehr gespannt, wie sich die Flussläufe präsentieren werden.

PS: Die Basislandschaft erzeuge ich per „Simplex Noise“, https://de.wikipedia.org/wiki/Simplex_Noise

PPS: Hier der Programmcode in Java – die einzige echte Änderung von mir zum Orginalcode ist, dass es für die Fließgeschwindigkeit jetzt einen Reibungswiderstand gibt und dass in der Iteration geprüft wird, ob ein Droplet den Bereich der Karte verlässt:

/*
* Based on public domain code from
*
* http://ranmantaru.com/blog/2011/10/08/water-erosion-on-heightmap-terrain/
*
*/
package heightfieldgenerator;

/**
* Note: In this code, the array is indexed with (x,z) and y is the height
*/
public class ErosionDeposit
{
private static final double LEVEL_EPSILON = 0.0001;

private Heightfield field;
private double [] erosion_r;
private double [] erosion_d;

private int hmapIndex(int x, int z)
{
return x + z * field.width;
}

private void depositAt(int x, int z, double w, double ds)
{
if(x >= 0 && z >= 0 && x < field.width && z < field.height)
{
float delta = (float)(ds * (w));
erosion_d[hmapIndex((x), (z))] += delta;
field.addHeight(x, z, delta);
}
}

private void deposit(int xi, int zi, double xf, double zf, double ds)
{
depositAt(xi, zi, (1 – xf) * (1 – zf), ds);
depositAt(xi + 1, zi, xf * (1 – zf), ds);
depositAt(xi, zi + 1, (1 – xf) * zf, ds);
depositAt(xi + 1, zi + 1, xf * zf, ds);
}

private void erode(int x, int z, double w, double ds)
{
if(x >= 0 && z >= 0 && x < field.width && z < field.height)
{
double delta = ds * (w);
field.addHeight(x, z, (float) -delta);

double r = erosion_r[hmapIndex((x), (z))];
double d = erosion_d[hmapIndex((x), (z))];
if (delta <= d)
{
d -= delta;
}
else
{
r += delta – d;
d = 0;
}

erosion_r[hmapIndex((x), (z))] = r;
erosion_d[hmapIndex((x), (z))] = d;
}
}

/**
* Height at location (x,z)
* @param x X
* @param z Z
* @return Height
*/
private double height(int x, int z)
{
// clamp coordinates
if(x < 0) x = 0;
if(z < 0) z = 0;
if(x >= field.width) x = field.width – 1;
if(z >= field.height) z = field.height – 1;

return field.getHeight(x, z);
}

public void dropletErosion(Heightfield field, int iterations, ErosionParameters params)
{
this.field = field;

double Kq = params.Kq;
double Kw = params.Kw;
double Kr = params.Kr;
double Kd = params.Kd;
double Ki = params.Ki;
double minSlope = params.minSlope;
double Kg = params.g;

erosion_r = new double [field.width * field.height];
erosion_d = new double [field.width * field.height];

int MAX_PATH_LEN = (field.width + field.height) * 4;

long t0 = System.currentTimeMillis();

long longPaths = 0, randomDirs = 0, sumLen = 0;

for(int iter = 0; iter<iterations; iter++)
{
if ((iter & 0x3FFF) == 0 && iter != 0)
{
System.err.println(„Calculating erosion:“ + (iter + 0.5) * 100 / iterations + „%“);
}

int xi = (int)(Math.random() * field.width);
int zi = (int)(Math.random() * field.height);

double xp = xi, zp = zi;
double xf = 0, zf = 0;

double h = height(xi, zi);
double s = 0, v = 0, w = 1;

double h00 = h;
double h10 = height(xi + 1, zi);
double h01 = height(xi, zi + 1);
double h11 = height(xi + 1, zi + 1);

double dx = 0, dz = 0;

int numMoves = 0;
for (; numMoves < MAX_PATH_LEN; ++numMoves)
{
// calc gradient
double gx = h00 + h01 – h10 – h11;
double gz = h00 + h10 – h01 – h11;
//== better interpolated gradient?

// calc next pos
dx = (dx – gx) * Ki + gx;
dz = (dz – gz) * Ki + gz;

double dl = Math.sqrt(dx * dx + dz * dz);
if (dl <= LEVEL_EPSILON)
{
// pick random dir
double a = Math.random() * Math.PI * 2;
dx = Math.cos(a);
dz = Math.sin(a);
++randomDirs;
}
else
{
dx /= dl;
dz /= dl;
}

double nxp = xp + dx;
double nzp = zp + dz;

// sample next height
int nxi = (int)(nxp);
int nzi = (int)(nzp);
double nxf = nxp – nxi;
double nzf = nzp – nzi;

double nh00 = height(nxi, nzi);
double nh10 = height(nxi + 1, nzi);
double nh01 = height(nxi, nzi + 1);
double nh11 = height(nxi + 1, nzi + 1);

double nh = (nh00 * (1 – nxf) + nh10 * nxf) * (1 – nzf) + (nh01 * (1 – nxf) + nh11 * nxf) * nzf;

// if higher than current, try to deposit sediment up to neighbour height
if (nh >= h)
{
double ds = (nh – h) + 0.001f;

if (ds >= s)
{
// deposit all sediment and stop
ds = s;
deposit(xi, zi, xf, zf, ds);
h += ds;
break;
}

deposit(xi, zi, xf, zf, ds);
h += ds;
s -= ds;
v = 0;
}

// compute transport capacity
double dh = h – nh;
double slope = dh;
//double slope=dh/sqrtf(dh*dh+1);

double q = Math.max(slope, minSlope) * v * w * Kq;

// deposit/erode (don’t erode more than dh)
double ds = s – q;
if (ds >= 0)
{
// deposit
ds *= Kd;
//ds=minval(ds, 1.0f);

deposit(xi, zi, xf, zf, ds);
dh += ds;
s -= ds;
}
else
{
// erode
ds *= -Kr;
ds = Math.min(ds, dh * 0.99f);

for(int z = zi – 1; z <= zi + 2; ++z)
{
double zo = z – zp;
double zo2 = zo * zo;

for (int x = xi – 1; x <= xi + 2; ++x)
{
double xo = x – xp;

double w1 = 1 – (xo * xo + zo2) * 0.25;
if (w1 <= 0)
{
continue;
}
w1 *= 0.1591549430918953;

erode(x, z, w1, ds);
}
}

dh -= ds;

s += ds;
}

// move to the neighbour
v += Math.sqrt(Kg * dh);

// Hajo: use friction
v -= v*v*0.05;

w *= 1 – Kw;

xp = nxp;
zp = nzp;
xi = nxi;
zi = nzi;
xf = nxf;
zf = nzf;

h = nh;
h00 = nh00;
h10 = nh10;
h01 = nh01;
h11 = nh11;

if(xi < 0 || zi < 0 || xi >= field.width || zi >= field.height)
{
// System.err.println(„Droplet #“ + iter + “ path left the field!“);
break;
}
}

if (numMoves >= MAX_PATH_LEN)
{
System.err.println(„Droplet #“ + iter + “ path is too long!“);
++longPaths;
}

sumLen += numMoves;
}

long t1 = System.currentTimeMillis();

System.err.print(„computed “ + iterations + “ erosion droplets in “ + (t1 – t0) + “ ms, %.0f droplets/s“);

System.err.println(“ “ + sumLen + “ average path length, “ + longPaths + “ long paths cut, “ + randomDirs + “ random directions picked“);
}

}

Ladenhüter, Teil 2


Letzte Woche hatte ich Zeit, den alten Programmcode mit den neuen Ladengrafiken zum Funktionieren zu bringen. Die neuen Regalfächer sind etwas kleiner als die alten, so dass die Bilder der Waren skaliert werden müssen – das ist nicht gut für die Bildqualität, aber ich denke, insgesamt immer noch akzeptabel.

Laden, Waren und Kunden
Laden, Waren und Kunden

Kunden gibt es jetzt auch und ebenfalls einen ersten Entwurf für den Handelsdialog.

Handelsdialog
Handelsdialog

Der Screenshot ist nicht ganz aktuell, sondern noch von einer Version, in der die Kunden nur gebrauchte Waren verkaufen wollten. Inzwischen wollen sie auch kaufen.

Ich bin noch am überlegen, wie man den Einkauf interessanter machen könnte. Irgendwo schwirrt die Idee eines Beratungsgessprächs in meinen Kopf herum, aber ich weiss nicht wie ich das im Spiel umsetzen könnte. Etwas in der Art:

Kunde: Ich suche Schätze in der alten Schlangengrube, und brauche dazu eine Ausrüstung mit hohem Giftwiderstand.

Händler: Bist du Magier? Dürfen es Metallgegenstände sein, oder eher Leder und Holz?

Kunde: Ich bin kein Magier. Metall ist in Ordnung, so lange der Preis stimmt.

Händler: Gut, ich hätte da folgendes im Angebot … (Hier müsste dann ein Dialogfenster kommen, mit einer Auswahl an Gegenständen, die eine gewisse Überlebenschance in der Schlangengrube gewähren).

Oder vielleicht auch so:

Kunde: Ich habe vom König den Auftrag seine Tochter zu befreien, und das Verließ wird von einem Feuer speienden Drachen bewacht.

Händler: Dann brauchst Du etwas mit Widerstand gegen Feuer. Bist Du eher Nah- oder Fernkämpfer?

Kunde: Sicherlich, ich will ja nicht gegrillt werden. Und Nahkämpfer.

Händler: Ich hätte da folgendes (Hier wieder der Dialog mit dem Angebot, Helm, Schild, Schwert, Rüstung usw. und einer Schätzung wie gut das gegen das Drachenfeuer hilft.)

Nicht, dass es dann eine Woche später so kommt:

Händler: Tut mir leid, Mumien werden hier nicht bedient.

Kunde (bandagiert): Scherzkeks. Ich bin der, dem Du die angeblich zu 98% feursichere Ausrüstung gegen den Drachen verkauft hast. Erinnerst Du dich noch?

Händler: Oh weißt Du, hier kaufen jeden Tag so viele Leute …

Kunde: Red keinen Unfug! Du weisst das ganz genau. Ich werde das öffentlich machen, was Du mir da für einen Schrott verkauft hast, dann kauft hier keiner mehr!

Händler: Ähem … was hälts Du von ein paar kostenlosen Heiltinkturen und einem Rabatt von 50% auf den nächsten Kauf? Und das Mißgeschick mit dem Drachen bleibt unter uns?

Schade, dass ich keine Vorstellung habe, wie man solche Dialoge von einem Programm erzeugen lassen kann …

PS: Die Kundengrafiken und der Zeichensatz sind nicht von mir:

Grafiken

Human silhouettes by Rejon (CC0/Public Domain) (https://openclipart.org/user-detail/rejon):
https://openclipart.org/detail/21966/person-outline-1
https://openclipart.org/detail/21968/person-outline-2
https://openclipart.org/detail/21970/person-outline-3
https://openclipart.org/detail/21972/long-haired-woman-outline

Monk by CDmir (CC0/Public Domain) http://opengameart.org/users/cdmir:
http://opengameart.org/content/monk

Brigand by Anthony Myers (CC-By 3.0) http://opengameart.org/users/anthonymyers:
http://opengameart.org/content/brigand

Mudeater by CDmir (CC0/Public Domain) http://opengameart.org/users/cdmir:
http://opengameart.org/content/mudeater-animated

Font

https://fontlibrary.org/en/font/medievalsharp

Copyright (c) 2011, wmk69 (wmk69@o2.pl),
with Reserved Font Names ‚MedievalSharp‘, ‚Medieval Sharp‘, ‚Medieval Sharp Bold‘
‚Medieval Sharp Bold Oblique‘, ‚Medieval Sharp Oblique‘.

This Font Software is licensed under the SIL Open Font License, Version 1.1.

 

Ladenhüter statt Abenteurer


Üblicherweise funktioniert mein Gedächtnis gar nicht schlecht, aber ich kann mich beim besten Willen nicht daran erinnern, ob ich hier im Blog schon mal über diese Idee geschrieben hatte …

In den meisten (Computer-) Rollenspielen übernimmt der Spieler die Rolle eines Abenteurers. Oft gibt es in diesen Spielen Läden, in denen der Spieler Ausrüstung erwerben kann.

Schon seit einiger Zeit geistert die Idee in meinem Kopf herum, hier einmal die Rollen zu tauschen, und den Spieler einen Händler spielen zu lassen. Letzten Sommer habe ich dann auf opengameart.org eine Diskussion zu dem Thema gestartet.

https://opengameart.org/forumtopic/need-game-design-help-with-shopkeeper-type-game

Die Entwicklung des Projekts ging langsam aber sie lief relativ gut. Ein kleineres Projekt, das tatsächlich die Chance hat, als Ein-Mann-Projekt auch fertig zu werden, vor allem, da ich viele Grafiken aus meinem vorigen „Juwelensuche“ Projekt wiederverwenden kann. Leider kam dann wieder einmal eine psychische Krise und eine lange Phase langsamer Erholung, und das Projekt lag vorerst mal auf Eis.

Inzwischen fühle ich mich wieder fit genug, daran weiterzumachen. Zumindest fühlte ich mich inspiriert, an besseren Grafiken für den Laden zu arbeiten, und habe zwei Designs entworfen. Zum einen einen Laden in einem Blockhaus:

Laden im Blockhaus
Laden im Blockhaus

Und einen Laden in einem aus Ziegeln oder Steinen gemauerten Haus:

Laden in Ziegelgebäude
Laden in Ziegelgebäude

Da ich den Laden im Blockhaus als zweiten angegangen bin, hat er bessere Grafiken für die Ladentheke und noch ein Holzfass ald Deko. Die Idee dabei ist, dass der Spieler als Händler nach und nach in bessere Gegenden der Stadt umziuehen kann, d.h. auch in höherwertige Gebäude, die mehr Miete kosten. Eine bessere Ladeneinrichtung soll dabei mehr Kunden und zahlungskräftigere Kunden anlocken.

Im Moment bin ich daran, weitere Deko-Gegenstände wie den Besen und das Fass zu entwerfen, damit der Laden nicht ganz so leer wirkt. Ein Buch für die Aufträge/Lieferungen und eine Art Kasse stehen als nächsten auf meiner List.

Wenn dann tatsächlich mal Waren zum Verkauf stehen, dürften die Läden dann auch interessanter aussehen. Hier noch ein Test mit der alten Ladengrafik und einem magischen Waldhorn für Barden.

Waren im Regal
Waren im Regal

Mit dem Programmieren läuft es noch nicht so gut wie mit den Grafiken, aber ich denke, da komme ich auch bald wieder hin.

Magische Silben


Rollenspiele aller Art haben oft Magiesysteme, seien es eine Reihe festgelegter Zauber, die der Spieler aufrufen kann, oder auch flexiblere Systeme.

Dungeon Master z.B. hatte ein System auf Basis von Silben und Symbolen, die zwar letztendlich auch nur Zugriff auf einen Katalog festgelegter Zauber boten, aber für den Spieler durch die Kombinationsmöglichkeiten zumindest den Eindruck eines flexiblen Systems hervorriefen.

Diablo 2/LoD hatte ein System von sogenannten Runenworten, welche die Eigenschaften von Gegenständen stark verbessern konnten. Das Prinzip war dem von Dungeon Master sehr ähnlich. Eine Rune bildete eine Silbe, und die richtige Reihung von Silben ergab ein magisches Wort, das dem damit verzierten Gegenstand magische Kräfte verlieh. Auch hier war die Flexibilität eine scheinbare, alle Runenworte mit ihren Wirkungen waren in einem Katalog hinterlegt. Es gab keine Logik, die aus der Kombination der Runen eine Wirkung ermittelt hätte, die über die Summe der einzelnen Runen hinausging – was ein im Katalog hinterlegtes Runenwort  praktisch immer hatte.

Der Hauptunterschied lag darin, dass in Dungeon Master die Reihung der Silben in Teilen vorgegeben war, weil die Silben in Gruppen angeboten wurden, und man Zaubersprüche immer mit einer Silbe aus der ersten Gruppe beginnen musste, gefolgt von einer Silbe aus der zweiten Gruppe usw., während die Reihung der Runen in Diablo 2 frei war – dennoch wirkte nur die richtige Reihenfolge, die dem Eintrag im Katalog entsprach, als magisches Wort.

Inspiriert von solchen Magiesystemen habe ich mich letztes Wochenende an den Entwurf eigener Symbole und Silben gemacht, und daraus ein kleines Handbuch der Silbenmagie gebastelt. Hier das Deckblatt und die erste Seite:

Magische Silben, Deckblatt
Magische Silben, Deckblatt

Da ich plante, das Dokument auf opengamert.org zu veröffentlichen, habe ich es auf Englisch verfasst. So etwas in einer Fremdsprache zu schreiben ist schwierig, weil man viel von dem Sprachwitz, den man in der Muttersprache beherrscht, in einer erlernten Sprache nicht so leicht zur Anwendung bringen kann.

Magische Silben, Seite 1
Magische Silben, Seite 1

Trotzdem, dass ich denke, auf Deutsch hätte ich den Text besser hinbekommen, bin ich mit dem Ergebnis eigentlich ganz zufrieden.

Das Titelblatt und alle sechs Seiten in hoher Auflösung und den zugehörigen GIMP Dateien mit editierbarem Text habe ich auf opengameart.org als Creative Commons veröffentlich:

http://opengameart.org/content/hajos-incomplete-book-of-magic

Danke auch an den Herausgeber der „Eagle Lake“ Schrifttype, dafür, dass er diese schöne Schrift frei verfügbar gemacht hat.

http://www.1001freefonts.com/eagle_lake.font