Roguelike-Diary (WIP ...)

Spiele, Demos, Grafikzeug und anderes unterhaltendes.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Oh Mann ... /:->
Die Realisierung der suchenden Monstern (wenn ein Monster den Spieler aus den Augen verliert, soll es anfangen nach ihm zu suchen - und zwar nicht mit den absoluten Koordinaten - das wäre einfach -, sondern mit den zuletzt bekannten) war ein einziger Alptraum!
Ich hab's mir einfacher vorgestellt. :?
Jetzt läuft es aber! :coderselixir: :lurk:

Gegeben sei:
Sobald ein Monster den Spieler sieht, speichert es seine Koordinaten in actor()\xSearch und actor()\ySearch. Diese Koordinaten vergisst es erst wieder, wenn es in den IDLE-Modus wechselt. Wenn jetzt der Spieler im Fog of War verschwindet, also canSee() ein #False zurückgibt, triggert der Suchmodus. Das Monster sucht so lange, bis es das gemerkte Feld erreicht. Oder halt, wenn das Monster auf seiner Suche den Spieler wieder sieht.

So sollte es ursprünglich laufen:
Im Such-Modus benutzen die Monster keine Dijkstra-Maps, sondern die aStar-Suche. Das brachte allerhand Probleme mit sich ... aStar ist super-fix, aber auch sehr streng ... man muß Situationen voraussehen und gewappnet sein ... was, wenn der Spieler auf seinem Weg eine Tür zuschlägt? Dann existiert kein Pfad mehr. Mit Dijkstra ist das kein Problem, dann wird automatisch der nächstliegende erreichbare Punkt angesteuert. Und wenn das Monster den Spieler durch eine Tür gesehen hat, und kurz darauf der Spieler diese Tür zuschlägt, dann hilft auch die Suche nach umliegenden Feldern nicht, weil diese ebenfalls hinter der Tür sind. Und im Nu hat man schönen modularen Code mit Hardcoding verunstaltet.
Ich habe verschiedene Lösungs-Ansätze probiert - der irrsinnigste beinhaltete, daß für jedes Monster, welches in den Suchmodus wechselt, eine eigene Dijkstra-Map angelegt wird. Dazu brauchte es aber einen internen Counter und eine Funktion, bzw. LinkedList, die immer die Übersicht behält, wieviele Monster gerade im Suchmodus sind, um dementsprechend das Array zu re-dimensionieren. Das war inaktzeptabel, da Dijkstra-Pfadsuche zwar extrem coole Möglichkeiten bietet, aber auch EXTREM die CPU belastet. 2,3 oder 4 Maps jeden Zug zu updaten fällt nicht ins Gewicht, darüber wird es langsam kritisch. Bzw. es wäre noch spielbar, aber die schnelle Punkt-zu-Punkt-Bewegung, auf die ich sehr stolz bin, würde wegfallen. Das wäre dann tatsächlich Schritt ... für Schritt ... für Schritt. "Zu schnell" ist mir lieber; stattdessen halte ich mir die Option offen, hinterher die Schrittgeschwindigkeit per Timer zu regulieren.

Die Lösung:
Ein guter Kompromiss: Es gibt nur eine einzige searchDijkstra()-Map! Das besondere an der searchDijkstra()-Map ist, daß diese mit Werten initialisiert wird, welche actor()-Elemente mit Kollision gänzlich ignorieren! Das hat den Effekt, daß die Dijkstra-Hotspots durch blockierende Elemente hindurch berechnet werden, also ein Monster auch bei versperrten Weg in der Lage ist, sich zumindest in der Nähe aufzuhalten (die Hotspots "leuchten" durch Wände).
Die searchDijkstra() wird NUR geupdatet, sobald ein Monster in den SEARCH-Modus wechselt oder diesen aktiv beendet (Zielpunkt/Spieler gesichtet)! Jedesmal wenn ein Monster auf die searchMap() zugreift, wird initialisiert, dann einmal durch alle Monster-actors() gecyclet (ich habe mir zu diesem Zweck ein *pointer-Array angelegt, mit dem ich schnell bestimmte actor()-Typen filtern kann), und jedes Monster, welches aktuelle xSearch,ySearch-Koordinaten hat, setzt seinen Hotspot auf die Map. Dann einmal durchrechnen, und das Ergebnis ist eine Map mit allen Hotspots, wo der Spieler kürzlich gesehen wurde; ein Monster im SEARCH-Modus wird sich automatisch den nächsten herauspicken und dort im Umkreis "gefangen" bleiben, bzw. patroullieren.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

***** VIDEO *****
Roguelike Diary 07: Enhanced Monster Intelligence

Heute habe ich was geschafft. 8)
Morgen mache ich mich dann an die Items ...

Und wer ein bißchen damit rumspielen will:
DOWNLOAD
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Roguelike-Diary (WIP ...)

Beitrag von Mijikai »

Sieht gut aus. :)

Hier noch ein Gedanke.

Weil die Darstellung so sprunghaft ist könnte es eventuell
besser sein das Ganze in 2 Schritten zu Rendern.
-> Spieler Zug
-> Monster Zug
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Du meinst die Fortbewegung, oder? :)
Wo du gerade davon redest ... habe gestern gemerkt, daß ich meine moveToAnimation()-Routine für den Nahkampf ohne weiteres für Schritt-Bewegungen nutzen kann. :-) Ist hier bei mir bereits implementiert. Nur für den Spieler hat sich das nicht bewährt, das fühlt sich "seltsam" und schwammig an. Aber die Monster gleiten jetzt schön (und auch noch schnell genug, daß man nicht wartet) - da hat's echt nicht viel gebraucht, und macht doch viel aus!
Kleiner unschöner Nebeneffekt, den ich aber nicht mit Priorität fixen werde: es fällt jetzt richtig auf, wenn ein Monster aus dem Fog of War "herauspoppt". Da geht noch was. Aber das kommt erstmal auf die lange Bank, während ich weiter an den Items arbeite ...
Heute war übrigens Bugfixing-Tag, da haben sich ein paar fiese emergent Effects eingeschlichen, die ich aber alle ausfindig machen und eliminieren konnte ... 8)
Morgen gibts ein Update!
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Abt. Emergent Bugs
Warum verschwindet ein Item aus dem Inventory, wenn man eine Tür öffnet und wieder schließt und währenddessen angegriffen wird? :?

Die Antwort:
Jedes Item (ebenso wie Hit-Animationen) sind geflaggt als Child-Actors vom jeweiligen Parent-Actor. Jedesmal wenn ein Actor in der actorFactory() gebaut wird, erhält er eine einzigartige Index-Nummer - und da ich nicht einfach nach oben zähle (da diese Nummern schnell inflationär ansteigen können, temporären Actors wie Hit-Effekten sei dank), habe ich ein dynamisches Array namens indexLibrary() erstellt, welches die index-Vergabe regelt: Wenn ein Actor erstellt wird, geht das Array einmal von unten nach oben durch und sucht den nächsten freien Platz; dieser wird geblockt und der Index an den Requester zurückgegeben. Falls kein freier Platz gefunden wurde, wird das Array mit ReDim um +1 vergrößert und der indexCounter auch; dieser neue Index wird zurückgegeben. Wenn jetzt ein Actor "gekillt" wird (Monster stirbt; der Timer der Hit-Effect-Animation ist abgelaufen, eine Tür wird geöffnet oder geschlossen, etc.), wird in der killActor()-Routine der jeweilige indexLibrary-Eintrag wieder entblockt.
Somit habe ich stets gewährleistet, daß ich nur die absolut notwendige Menge an Speicher verbrauche.
Der Casus Knacktus ist der, daß beim Aufrufen der actorFactory() ein Child-Actor denselben Index wie sein Parent-Actor erhält. Deswegen kann ich, wenn ein Parent-Actor gelöscht wird, auch gleich alle damit verknüpften Child-Actors aufrufen, und schöne Sachen mit ihnen machen (ebenfalls löschen, oder Items droppen lassen, etc.). Momentan werden diese einfach mitgelöscht. Wenn jetzt z.B. der Spieler den index = 44 mitbekommen hat, und er einen Schild aufhebt, wird dieser Schild als Child von 44 geflaggt. Wenn er wieder gedroppt wird, wird er auf Null zurückgesetzt (Items sind eine Ausnahme, die haben default-mäßig keine eigene Nummer, nur 0 = liegt im Dungeon und # = im Besitz von #).
Und jetzt wirds interessant:
Wenn der Spieler angegriffen wird, wird ein Animations-Actor (Child) mit Index von 44 (Spieler-Index) erstellt. Wenn jetzt der Spieler schnell genug die Tür öffnet, bevor der Timer der Hit-Animation abgelaufen ist, passiert nichts besonderes: Der Spieler öffnet die Tür (index = 33). Diese wird gelöscht, und an ihrer Stelle wird ein neuer Offener-Tür-Actor mit der nächsten freien index-Nummer erstellt (z.B. 55). Wenn man jedoch mit dem Öffnen wartet, bis der Hit-Effekt abgeklungen ist, hat der Animations-Actor in der kill-Actor-Routine das Feld Nr. 44 der indexLibrary() bereits entblockt. Das heißt, wenn ich jetzt eine Tür öffne, ist der nächste freie index 44. Dort wird ein offener Tür-Actor erstellt. Wenn ich diese wieder schließe, wird sie gelöscht, und da ein Tür-Actor nicht als Child-Actor geflaggt ist, darf er gleichzeitig einmal das Pointer-Array von #isChild durchlaufen und alle assoziierten Actors löschen.
Darunter eben auch den Schild, den der Spieler weiter oben aufgesammelt hat.
:bluescreen:

Daran saß ich gerade 2 Stunden ... alles was fehlte war eine simple Abfrage in der killActor()-Prozedur, daß Child-Actors den LibraryIndex nicht verändern dürfen. :coderselixir:
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

***** VIDEO *****
Roguelike Diary 08: Items and Basic Inventory

Ich habe mir die Option offen gehalten, daß Monster auch Items aufsammeln können - allerdings werden diese nur zum verfügbaren Inventory Space addiert, und nicht, wie beim Spieler, in Slots abgelegt.
Momentan hat man 4 Slots: Nahkampfwaffe, Schild, Fernkampfwaffe und Munition. Wenn man ein Item aufnimmt, wird es im entsprechenden Slot abgelegt. Ist dieser Slot belegt, wird das dort liegende Item gedropt und man rüstet das neue aus. Munition ist geflagt als "stackable", das heißt die Stacks des aufgehobenen Items werden zu den Stacks des bereits vorhandenen Items aufaddiert.
Später möchte ich das System flexibler ausbauen: es soll unterschieden werden zwischen Inventory Slots und Equip Slots; und daß mehr als ein Item auf demselben Feld liegen darf (Container!). In meinem Blitzbasic-Projekt hatte ich das bereits implementiert, das ist eine recht komplexe Angelegenheit aber absolut schaffbar, hat nur momentan keine Priorität, da das jetzige System für die Fertigstellung eines MVP (Minimum Viable Products) absolut ausreichend ist.

Weitere major Tweaks:
- Monster springen jetzt nicht mehr von Feld zu Feld, sondern gleiten schön.
- Bei der Aktualisierung der InteractMap() wird jetzt auch abgefragt, ob man eine Fernkampfwaffe und Munition ausgerüstet hat - falls nicht, kann man das entsprechende Monster gar nicht erst anvisieren.

Next in Line:
Speichern und Laden des Spielstandes ...
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

So, man kann jetzt den Spielstand speichern und laden ...
Würde mich freuen, wenn jemand ein bißchen rumprobieren würde, ein paar Items aufsammeln, Monster herumjagen, speichern und wieder laden, und dann Rückmeldung gibt, ob was Merkwürdiges aufgefallen ist - also was das korrekte Wiederherstellen vom Spielstand angeht; Kollisionen, Items, etc. alles noch an Ort und Stelle? Ich habe ganz schön Gehirnschmalz verbraten, um's möglichst modular zu gestalten, daß ich das später leichter umschreiben kann, wenn's denn mal mehrere Dungeon-Level geben sollte. :)
Bitte keine Kommentare zu Spielbalance, oder Level-Design, das sind nämlich beides non-existente Faktoren ... /:-> prozedurale Level-Generierung ist der nächste große Milestone, den ich evtl. schon nächste Woche angehen will. Aber bevor ich mich da dran wage, will ich sicher gehen, keinen groben Unfug gebaut zu haben, der sich evtl. später rächt.
1001 Dankeschön! :)

DOWNLOAD

Linksklick: Bewegen/Klick auf Fenster um zum Spiel zurückzukehren, wenn ReleaseMouse = #True
Rechtsklick: Interagieren/Items aufheben/Angreifen (Fernkampf nur möglich, wenn ihr den Bogen und Pfeile im Inventar habt)
W,S,A,D: Kamera bewegen
Leertaste: Kamera auf Spieler zentrieren
C (neues Feature): Zentriert Kamera auf gegenwärtige Mouse-Pointer-Location (Limit-Abfrage, daß Spieler im Viewport bleibt) 8)
-/+ Taste: Leuchtradius um Spieler verkleinern, bzw. vergrößeren (wird nicht gespeichert, da noch keine spielerische Relevanz)
Speichern: F5
Laden: F9
ESC: Spiel beenden

//EDIT: Ihr habt 1 Mio Lebenspunkte. :lurk:
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Ich habe gerade selbst noch einen Bug ausfindig machen und beheben können, welcher dafür sorgte, daß das Startfeld immer mit PermaCollision geflaggt wurde (hat spielerisch keine Auswirkung, da der Wert nur für die Berechnung der Dijkstra-Maps relevant ist), und der Spieler seine Kollision verliert - meistens merkt man das im Spiel nicht, kann aber hin und wieder zu erratischem Verhalten der Monster im Such-Modus führen, wenn man sich nahe des Startfeldes aufhält.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Roguelike Diary 09: Procedural Generation - Basic Branching

Part 1 von meinem Dungeon-Generator ist fertig. Oben könnt ihr euch 100 damit erstellte Layouts ansehen. :)
Da geht aber noch mehr ("Innenarchitektur", Loops, interessantere Raumformen, etc.) ...
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Antworten