Worklog:
• Man kann jetzt zwischen Dungeon-Ebenen "reisen". Das heißt, wenn man den Ausgang gefunden hat, wird ein neuer Dungeon erstellt, bzw. falls die Zielkoordinate des Exit-Teleporters ein #True zurückgibt, wird stattdessen die existierende Dungeon-Ebene geladen.
• Ich arbeite ausschließlich mit lokalen Daten: Beim "reisen" wird die aktuelle Ebene (floorData) auf Festplatte gespeichert, und nur die Spieler-relevanten Daten werden in die nächste Ebene übernommen. Das hat den Vorteil, daß meine Listen schön klein bleiben, und ich muß mir keine Sorgen über Zugriffsgeschwindigkeiten machen.
• Ein paar Fallstricke bringt das System mit sich, z.B. muß man sich genau Gedanken machen, welche Daten relevant sind, welche überschrieben werden, wie ich gewährleiste, daß wirklich alles gelöscht, bzw. geladen wird, in welcher Reihenfolge Variablen initialisiert werden, Fragen wie: muß diese Variable/Liste, etc. wirklich gespeichert werden, oder kann ich deren Zustand anhand bestehender Spieldaten wiederherstellen?
• Zu dem Zweck habe ich mir jetzt eine Prozedur namens "Game Manager" gebastelt, die alle diese Dinge organisiert. Wenn immer geladen, gespeichert, ein neues Spiel gestartet, in anderen Worten, die GameLoop verlassen wird, wird ein sogennanter "Job" kreiert, welcher vom gameManager() an die zuständige Instanz weitergeleitet wird.
Folgende Jobs existieren derzeit:
Code: Alles auswählen
Global jobDescription
Enumeration 0
#nullJob
#newGameJob
#loadJob
#exploreJob
#travelJob
#saveJob
#quitJob
EndEnumeration
#nullJob
Der Debugger schlägt Alarm.
#newGameJob
Das aktuelle Spielobjekt schreddern und ein neues Spiel starten.
#loadJob
Das aktuelle Spielobjekt schreddern und einen Spielstand laden (coreData UND floorData).
#exploreJob
Eine neue Ebene wird betreten; der Dungeon existiert noch nicht und muß erstellt werden.
Aktuelle floorData muß gespeichert werden
#travelJob
Eine neue Ebene wird betreten; die Ebene existiert bereits und wird geladen.
Aktuelle floorData muß gespeichert werden.
#saveJob
Spielstand speichern (coreData UND floorData)
#quitJob
Duh.
Weitere denkbare Jobs für die Zukunft:
#restartCampaignJob
Für den Fall, daß ich mich für ein Modell nach DIABLO-Vorbild entscheide, bei dem man nach Spielende einfach von vorne weiterspielen kann, ohne daß Level-
Progression und Inventar gelöscht werden.
#deleteSavesJob
Was wäre das für ein Roguelike ohne
Permadeath?
Mein Creator-Tool ist jetzt in der Lage, bessere und interessantere Loops zu kreieren. Bislang funktioniert das ausschließlich über ein
brute force Verfahren, das zufällig gesucht hat, wo man 2 Parts vom Dungeon durch Graben eines Ganges verknüpfen kann und dies solange wiederholt hat, bis eine ge
seedete Obergrenze an Loops erstellt wurde (anschließend wurde zerstörte Architektur gefixt).
Ich habe das Verfahren dahingehend ge
tweaked, daß vorm Graben selektiert wird, ob beide Parts des Dungeons einen unterschiedlichen Index haben, außerdem wird vor dem Graben ein A*-Search von Punkt a zu b durchgeführt und für die Aktion ein "Evaluierungs-Score" vergeben: je weiter der reguläre Weg und umso kürzer der zu grabende Loop, umso höher der
Score:
Abschließend einen Überblick über in die Tools, welche mir zur Verfügung stehen, wenn es ans Erzeugen von Zufallszahlen geht:
Code: Alles auswählen
Procedure weightedRoll(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10)
Define nMax
Define nDice
Define nAdd
Dim chance(10)
Define a
chance(1) = n1
chance(2) = n2
chance(3) = n3
chance(4) = n4
chance(5) = n5
chance(6) = n6
chance(7) = n7
chance(8) = n8
chance(9) = n9
chance(10) = n10
For a = 1 To 10
nMax = nMax + chance(a)
Next
nDice = Random(nMax,1)
For a = 1 To 10
nAdd = nAdd + chance(a)
If nAdd >= nDice
ProcedureReturn a
EndIf
Next
EndProcedure
weightedNumber(0,4,1,0,0,0,0,0,0,5) gibt mir entweder eine 2,3 oder 10 zurück mit entsprechenden Wichtungsfaktoren:
2 = 40% Chance
3 = 10% Chance
10 = 50% Chance
Code: Alles auswählen
Procedure gaussRoll(minValue,maxValue,ceilWeight)
Define newMax
Define add
Define sum
Define dice
Define a
Define n
newMax = (maxValue-minValue)+1
sum = (newMax+1)*(newMax*0.5)
dice = Random(sum,1)
For a = 0 To newMax
add = add+a
If add >= dice
If ceilWeight
ProcedureReturn (a + minValue)-1
Else
If Not ceilWeight
ProcedureReturn ((newMax-(a-1))+minValue)-1
EndIf
EndIf
EndIf
Next
EndProcedure
Simuliert einen "Kleinen Gauss-Würfel":
gaussRoll(1,5,#True) erzeugt einen 15 seitigen Würfel, von dem 5 Seiten eine 5 haben, 4 eine 4 und so weiter bis zur 1, von der nur eine Fläche existiert. Mit diesem Würfel wird einmal gewürfelt und die Zahl zurückgegeben. Dies ist in beide Richtungen möglich:
gaussRoll(2,3,#False) erzeugt mit 66% Wahrscheinlichkeit eine 2, und mit 33% Wahrscheinlichkeit eine 3.
Code: Alles auswählen
Procedure bellCurveRoll(min,max)
Define value = max-min
Define modValue = Mod(value,2)
Define dice
value/2
dice = Random(value)+Random(value+modValue)+min
ProcedureReturn dice
EndProcedure
Teilt jede beliebige Differenz auf zwei möglichst gleichflächige Würfel auf und gibt mir eine zufällige Zahl gemäß der Gauß'schen Normalverteilung wieder.
Code: Alles auswählen
Procedure wellCurveRoll(min,max)
Define lowMedian, highMedian
Define absLow, absHigh
Define minusDice = 0
Define plusDice = 1
Dim dice(1)
lowMedian = Round((max+min)/2,#PB_Round_Down)
highMedian = Round((max+min)/2,#PB_Round_Up)
dice(minusDice) = Random(lowMedian,min)
dice(plusDice) = Random(max,highMedian)
absLow = Abs(dice(minusDice)-lowMedian)
absHigh = Abs(dice(plusDice)-highMedian)
If absLow = absHigh
ProcedureReturn dice(Random(plusDice,minusDice))
EndIf
If absLow > absHigh
ProcedureReturn dice(minusDice)
EndIf
If absHigh > absLow
ProcedureReturn dice(plusDice)
EndIf
EndProcedure
Das gleiche, nur invertiert: Die Funktion erzeugt bevorzugt "extreme" Werte mit Wichtung zu beiden Enden der Kurve.