Spielereien vom diceman

Heißer Scheiß von übermorgen oder liebenswerte Goldies von vorvorgestern: Alles willkommen!
Antworten
Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Mi 5. Aug 2020, 22:41

UPDATE:

- Habe eine Möglichkeit gefunden, in allen Verarbeitungs- und Darstellungs-Routinen nur diejenigen Objekte durchzuiterieren, welche in der jeweiligen Map vorhanden sind, ohne dafür die Objekte in eine globale und lokale Liste aufsplitten zu müssen (das würde spätestens, wenn Objekte während des Spiels gelöscht/kreiert werden, extrem messy werden) - der essentielle Trick war, bei Betreten einer neuen Map den stage-Parameter aller Objekte der aktuellen Map auf -1 zu setzen, dann die Liste mit

Code: Alles auswählen

SortStructuredList(listName(), #PB_Sort_Ascending, OffsetOf(STRUCTURE\stage), TypeOf(STRUCTURE\stage))
nach Größe des stage-Index zu sortieren, und anschließend alle -1 Paramter zurück auf den Index der aktuellen Map setzen .... Voila, alle Objekte der aktuellen Map befindet sich am Anfang der Liste, und in den jeweiligen Verarbeitungsroutinen kann ich nun, sobald ein stageIndex <> currentMap auftaucht, die ForEach/Next-Schleife vorzeitig canceln. Theoretisch ist es so möglich, ein Modul mit 1000 und mehr Maps laufen zu lassen ohne daß die CPU einknickt - irgendwann wird natürlich der Arbeitsspeicher knapp. :think: Diesen Schritt muß man einmalig beim Betreten einer neuen Map ausführen, und ich darf ab sofort nicht mehr vergessen, Objekte, die innerhalb des Spiels erzeugt werden, umgehend mit MoveElement() an den Anfang der Liste zu schieben, damit sie sich im gegenwärtigen Verarbeitungskreislauf befinden. :shifty:
John Carmack wäre wahrscheinlich trotzdem stolz auf mich. :mrgreen:

- Neu sind Wegsteine, die man über einen Text-Button mit einer Inschrift versehen kann - wichtig für Atmosphäre und den Adventure-Charakter des Spiels! Das ging überraschend elegant mit den Gadget-Routinen von "PureBasic": ein neues Fenster öffnet sich, in dem man seinen Text schreiben kann, und sobald das Fenster geschlossen wird, werden die ersten 5 Zeilen per GetGadgetItemText() ausgelesen und als String im zugehörigen inscription()-Objekt gespeichert. Editieren geht auch: einfach einen beschriebenen Wegstein mit dem Text-Tool anklicken, und das Bearbeitungs-Fenster öffent sich wieder. Inschriften sind verlinkt mit den Steinen, also wenn man einen Stein löscht, verschwindet auch die Inschrift.
Und wenn man im Editor mit dem Maus auf einem beschriebenen Wegstein zeigt, wird, falls vorhanden, der zugehörige Text in einer Vorschau eingeblendet.

- Was jetzt noch fehlt, ist ein Message-System, mit dem man dem Spieler WÄHREND dem Spiel Informationen und Nachrichten zukommen lassen kann ... die schönsten Wegsteine nutzen nichts, wenn man sie nicht lesen kann ... das geschieht im nächsten Schritt, und dafür muß ich mich auf die Suche nach einem schicken Bitmap-Font machen.

Clipboard14.png
Clipboard13.png

- Habe die Säulen-Grafiken überarbeitet - mit 16x16 Bildpunkten kannst nur soviel anstellen, aber es sieht jetzt besser aus, finde ich. Habe mich bewußt eingeschränkt, da ich so auch als Nicht-Grafik-Profi relativ schnell brauchbare Ergebnisse erziele und es so leichter ist, einen konsistenten Stil aufrechtzuerhalten. Grundsätzlich macht mir der Design-Aspekte, trotz kleiner, bescheidenen Möglichkeiten, sogar Spaß.
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Do 6. Aug 2020, 23:49

So langsam tut sich was im Dungeon ...
Das Message-System ist implementiert! 8-)
In "PureBasic" gibt den den tollen Befehl RegisterFont(), damit lassen sich Fonts spezifisch für das Projekt initialisieren, ohne daß der Endnutzer diese installieren muß. Sie werden lediglich für die Anwendung "registriert".
Ich habe es maximal modal gecodet - also der Routine createMessage() wird lediglich die entsprechenden Zeilen-Strings mitgeteilt, und aus den übermittelten Daten wird abhängig von Zeilenlänge, Anzahl und gewähltem Schriftfont die korrekte Höhe und Breite der Message-Box ermittelt - sollte sich später der Schriftfont ändern, muß ich an der Routine nichts mehr herumfuckeln - "it just works". Die Message-Box wird zudem "intelligent" platziert: ist der Spieler in der oberen Hälfte des Screens, wird die Box zu seinen Füßen angezeigt, befindet er sich unterhalb der Screen-Mitte, wird sie über ihm angezeigt.

Clipboard15.png

WEITERE UPDATES:
- Einen letzter Obstacle-Akteur zugefügt: ein Kerzenleuchter.
- Im Editor platzierte Teleport-Traps haben im kompilierten Modul keine absolute Location. Es werden lediglich beim Initialisieren der Map alle vorhandenen Teleport-Traps gezählt, dann werden sie gelöscht, und zufällig neu verteilt. Die einzige Regel ist, daß lediglich in diagonalen Richtungen blockierende- und Special-Tiles angrenzen dürfen (Special = begehbar, mit Effekt; z.B. offene Türen werden auch als Special-Tile gewertet). So bleibt eine 100% Navigation gewährleistet.
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Fr 7. Aug 2020, 23:17

UPDATE:
- Habe meine 16 Farben als konstanten Datensatz im Programmcode hinterlegt, so daß sich jegliche Grafik-Operationen (inklusive Text-Darstellung) stets kohärent zur Palette extern eingelesener Sprites verhalten.
- Jeglicher Ingame-Text, der nicht individuell im Editor als Inschrift "designt" wurde (HUD-Infos, Eigennamen, generische Interaktionen, etc.) wird zu Beginn des Spiels in dimensionierten Strings eingelesen, in einer Data-Sektion, welche zudem die Funktionalität für verschiedene Sprachen unterstützt. Die Sektion funktioniert autonom und leicht zu warten - eventuell kann man die Strings auch später über eine seperate .txt-Datei einlesen:

Clipboard16.png

- Die Outdoor-Tiles werden nicht länger als obstacle()-Akteure geführt, sondern gehören der Base-Klasse an, funktionieren also intern wie ein weiteres Wall-Tileset. Damit zusammenhängend mußte ich ein paar Ausnahmen coden, z.B. haben Bäume keine perspektivische Frontansicht, und der Grass-Untergrund ist als Backgroundgrafik fix gesetzt. Außerdem funktionieren "Geheimgänge" etwas anders: jeder actor() hat jetzt ein individuelles Alpha-Flag, und geheime Durchgänge im Wald sind ein kleines bißchen transparent ... wie bei den regulären Geheimgängen muß man aber auch hier genauer hinschauen um sie zu entdecken ... :)

Clipboard17.png
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Sa 8. Aug 2020, 15:38

Clipboard18.png
Clipboard19.png

UPDATE:
Erweiterte interaktive Message-Funktionen sind fertig!
Diese sind fester Systembestandteil für diverse Gameplay-Mechaniken (Türen mit Gewalt öffnen/Schrein-Interaktion) und können nicht im Editor erstellt werden. Die Routine nutzt jedoch dieselben Bausteine der Standard-Message-Funktion, ist also maximal modal designt. Die displayMessage()-Routine erkennt, ob sie eine reine Text-Message wiedergibt, oder eine mit interaktiven Elementen, und ist somit in der Lage einen Wert an den Requester zurückzugeben, ob und welche Option ausgewählt wurde.
- Blutsplitter sind die Währung im Spiel - man kann sie im Dungeon finden, und sie werden von besiegten Monstern gedroppt. Mit ihnen kann man sich die Gunst der Götter erkaufen, sie dienen aber auch als Punkte-Elemente (mit der Option "Inschriften entziffern" kann man sich die Highscore-Liste anzeigenlassen).
- Da mein erklärtes Ziel weiterhin darin besteht, das originale "Kalawaum" zu re-designen, wird an Schreinen zunächst nur die "Inschriften entziffern" und die "Opfer darbringen"-Option angeboten. Der Rest ist Design-Space für eigene Ideen um das Gameplay aufzupeppen. Das soll jetzt aber erstmal nicht der Fokus sein.
- Ich habe einen Datensatz an Alten Göttern eingelesen - für jede Session werden 4 zufällig ausgewählt, von denen Schreine im Spiel zu finden sein werden. Götter sind unterschiedlich gut (oder schlecht) gelaunt - während einer Session bleibt die Stimmung eines Gottes immerhin konsistent, also man muß in jedem Spiel neu lernen, welcher Gott wie drauf ist. :shifty: Meine Idee für ein besonderes, an Schreine gekoppelte Objective werde ich aber ebenfalls zurückstellen, und stattdessen mit Hochdruck an der Fertigstellung eines spielbaren Prototypen werkeln ... wenn alles weiter so gut läuft, vielleicht schon Ende August, aber spätestens September! :thumbup:

Clipboard20.png
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Sa 8. Aug 2020, 23:05

Clipboard21.png

UPDATE:

Meine Animations-Routine ist fertig!
Bislang kann sie zwar nur das Hinabstürzen in Löcher darstellen und einen Delay abspielen, aber jetzt, wo die grobe Architektur steht, kann ich ihr leicht neue Aufgaben zuteilen. Und viel komplexer als der Sturz in ein Loch wird es auch nicht - das war schon mit der aufwendigste Task, und die Hauptarbeit bestand hier auch nicht in der Darstellung, sondern im korrekten Konfigurieren der Sprites (jedes Monster-Sprite bekommt bei Erstellung eine prozedural erstellte Fall-Animation zugeteilt).
Es war in sofern wichtig, daß ich mich der Aufgabe zuwende, da ich nun im nächsten Schritt das erste Monster in den Dungeon einführen werde. Da der Spieler auch der Monsterklasse angehört, können prinzipiell alle Aktionen und Effekte, welche auf den Spieler wirken, auch auf Monster-Objekte angewendet werden - und um hier sicherzustellen, daß alles reibungslos funktioniert, kann ich mit dem Schritt nicht zu lange warten, da er von allen Aspekten des Spiels das weiteste Interaktions-Potential mit diversen Routinen und Gameplay-Mechaniken besitzt.
Da muß alles reibungslos laufen, bevor ich weiteren autonomen Content hinzufüge. :thumbup:

Clipboard23.png
Clipboard22.png

- Außerdem kleiner Tweak: da ich in einem für die jeweils aktuelle Map lokal angelegten monsterMap()-Array stehts Buch darüber führe, welche Felder durch Monster besetzt sind, kann ich easy und schnell Akteure, welche sich auf denselben Feldern wie Monster befinden (z.B. Pentagramm-Schalter und Items), mittels Alpha-Value teil-transparent machen - kostet quasi keine Zeit, sieht cleaner aus, und Hintergrund und Monster-Sprite verschwimmen nicht zu einem Pixelwust.
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » So 9. Aug 2020, 19:23

Clipboard24.jpg

"Ladies and Gentleman, the first Monster has just entered the dungeon ..."
Das war eine Aufgabe für die ganze Nacht ... habe bis morgens früh um 5:00 durchprogrammiert, bis das alles so funktionierte wie gedacht (NullPointer-Exceptions, Array Index out of Bounds-Meldungen, etc.), aber jetzt habe ich die komplette Monster-Navigation fertig. :geek: :geek:
Monster haben verschiedene states: wenn sie "geboren" werden, schlafen sie, bis der Spieler sie aufweckt (wenn er ein Feld neben ihnen betritt). Dann bekommt das Monster den Zustand "aufgewacht", der Spieler hat also die Wahl ob er direkt draufhauen, oder weglaufen will. Im nächsten Schritt wechseln Monster in den Verfolger-Modus ... die KI und Wegfindung ist rudimentär, leistet aber ihren Zweck, und viel mehr hatte das Original auch nicht zu bieten - Ich habe das so gelöst:

Monster suchen die 8 umliegenden Felder ab, und geben jedem Feld einen Wichtungsfaktor, abhängig von der Distanz jedes Feldes zum Spieler im kartesischen Koordinatensystem (Satz des Pythagors, anyone?). Die Formel hierfür lautet:

Sqr(Pow(x-targetX, 2) + Pow(y-targetY, 2))

Für jedes Feld wird ein dummy()-Element angelegt, welches die Koordinaten enthält, sowie den Wichtungsfaktor. Dann werden alle Elemente herausgeschmissen, für welche Distanz > Minimum. Von den verbleibenden Feldern (in der Regel 1-2) sucht das Monster eines zufällig heraus und bewegt sich dorthin. Das reicht, um dem Spieler auf den Fersen zu bleiben, aber hat man es einmal abgeschüttelt, ist es in der Regel in seiner Ecke gefangen, solange bis man im großen Bogen zurückkommt.

Und da kommt der nächste monster()\state ins Spiel, der "Such"-Modus: Sobald nämlich das Monster mindestens 2 Felder weit entfernt vom Spieler ist, werden bei der Umgebungssuche die letzten 4 besuchten Felder mit einem abnehmenden Penalty-Wert belegt. Mit dem feinen Trick habe ich natürlich keine Skynet-KI geschaffen, aber die Monster sind jetzt mitunter in der Lage weiträumiger nach Umgehungen zu suchen, anstatt nur mit dem Kopf vor die Wand zu laufen. Der "dumme" Verfolger-Modus bei unmittelbarer Distanz hat aber auch seinen taktischen Zweck - würde man hier die KI "einschalten" würde das Monster, wenn man um ein Hinternis herumläuft, immer nur stupide in einer Richtung hinterherlaufen, ohne die Richtung abzuwechseln.

Puh, weitere Tweaks:

Es gibt eine Variable maxActiveMonsters, welche vorgibt, wieviele Monster gleichzeitig aktiv sein dürfen. Zu Beginn des Spiels ist dies immer 1. Also wenn man vor einem Monster wegläuft, und man weckt ein weiteres auf, wird das erste Monster in den Zustand "Lauern" versetzt (Lauern ist effektiv wie schlafen, aber die Monster wissen bereits vom Spieler), und das neu aufgeweckte Monster "übernimmt". Und jetzt kommt das große Aha: wenn ein Monster stirbt, wird automatisch gecheckt, ob sich auf der Map Monster im "Lauern"-Modus befinden, und wenn ja, werden von denen soviele aufgeweckt wie die maxActiveMonsters-Variable erlaubt. Also zu Beginn nur eines.
Mit maxActiveMonsters (etwas ähnliches gab es auch im Originalspiel, aber mein System ist deutlich elaborierter) lässt sich prima die Gefährlichkeit des Dungeons an den gegenwärtigen Power-Level des Spielers anpassen: jede Map hat eine min/max-Range wie stark die Monster mindestens sind, und sein können. Und da der Spieler effektiv auch nur ein Monster ist, und somit irgendwo auf dieser Skala zu finden ist, kann man den maxActiveMonster-Wert dynamisch anpassen. maxActiveMonsters wird einmal gecheckt, wenn man eine Map neu betritt, und dann jedesmal aktualisiert, wenn ein Monster stirbt.

Verlässt man eine Map, werden alle Monster, deren\state >= #monsterStateLurking ist, in den Zustand "Lauern" versetzt. Also man kann die Monster so vorübergehend "abschalten", und hat beim Neu-Betreten der Map auch erstmal seine Ruhe, bis man ein Monster erfolgreich besiegt - dann wachen unter Umständen eines oder mehrere (abhängig von maxActiveMonsters) lauernde Monster auf, um ihren Kameraden zu rächen.

Monster haben eine Animation beim Laufen (also keine wirkliche Animation im Sinne von Gliedmaßen bewegen, duh), aber das Sprite bewegt sich halt kontinuierlich aufs nächste Feld. Animationen werden nur ausgeführt, wenn auf entsprechenden Feldern der Fog of War aufgedeckt ist; also wenn man kein Monster sieht, werden für diese auch keine Animations-Berechnungen ausgeführt. Für den Spieler habe ich die Bewegungsanimation ohnehin deaktiviert - der bewegt sich statisch von Tile zu Tile, das fühlt sich besser, flotter und in jeder Hinsicht "knuspriger" an.

Bislang ist es nur möglich, Monster in Fallen zu locken (dies funktioniert hervorragend, inklusive Animation) - Monster teleportieren sich auch munter durch die Gegend, wenn sie auf eine Teleport-Falle treten - also meine Vorarbeit in dieser Richtung hat sich voll ausgezahlt.
Im nächsten Schritt werde ich das Kampf-System entwickeln.
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Mo 10. Aug 2020, 23:38

Clipboard25.png

UPDATE:

- Das wichtigste zuerst (warum hat mich eigentlich keiner darauf angesprochen?), DER DUNKELDÜRRE HAT NUR EINEN FLÜGEL!!1!
Ist jetzt zum Glück gefixt!

- Das "Bump"-System ist voll entwickelt! Das heißt, das Spiel registriert, wenn der Spieler in ein Monster rennt (und vice versa). Damit zusammenhängend hatte ich ein schönes Erlebnis bezüglich emergent behavior: ich hatte die Kollisions-Abfrage für das monsterMap()-Array herausgenommen, damit Kollisionen mit Monstern überhaupt als solche erkannt werden; hatte aber nicht bedacht, daß das Array nicht zwischen Monstern und Spielern unterscheidet, also begannen die Monster munter sich gegenseitig anzugreifen im Versuch, auf die entsprechenden Felder zu gelangen. Komplett mit allem was dazugehört: Schaden austeilen, Blut-Animation, Sterben, XP für den Spieler. Das sind solche raren Momente, da fühlt man sich so:
8884777495.jpg
auch wenn es nicht beabsichtig war, und ich es wieder deaktiviert habe.

- Blutgrafik zugefügt, wenn ein Charakter Schaden nimmt.

- Monster haben kleine Healthpoint-Bars! Der Spieler benötigt Feedback, wieviel Schaden er anrichtet, und das antiquierte Text-Message-System des Originals ("Du hast so und soviel Schaden gemacht, das Monster hat jetzt noch so und soviel Kraftpunkte") ist, nun ja, antiquiert. Die Frage war, wie ich das umsetze, also ob jedes Monster sein eigenes HUD-Element hat, wie ich die Prozentanzeige aktualisiere, etc. Habe mich letztendlich für die bequemste und schnellste Möglichkeit entschieden: 20 Healthbar-Sprites werden vorgerendert, mit Anzeige in 5%-Abständen, und in der drawScreen()-Routine, wenn durch die Monster iteriert wird, kann mit (hp*100)/hpMax/5 bequem und schnell der Index des korrekten Sprites herausgesucht und angezeigt werden. Bei so kleinen Grafiken die korrekte Prozentanzeige jedesmal "in Echtzeit" nachzujustieren, wäre Design-Overkill - die festen 5% Stufen sind absolut ausreichend um anschaulich zu vermitteln, wie effektiv der eigene Angriff war. Später, in der Polish-Phase, kommen noch Floating Numbers hinzu. :thumbup:

- Die Healthbar-Anzeige wird dynamisch angezeigt, das heißt, nur bei aktiven Monstern ist sie zu sehen, und nachdem ein Monster 5 Schritte gelaufen ist, wird sie ausgeblendet. Sobald ein Monster angreift, oder der Spieler ein Monster, oder ein Monster geweckt wird, wird die jeweilige Anzeigen-Abfrage wieder auf #True gesetzt, und der Schritt-Counter resettet.

- Außerdem habe ich das Kampf-System grob entwickelt. Man kann zwar noch keine Waffen und Schilde aufsammeln, die entsprechende Funktionalität ist aber implementiert und ich kann Monster und Spieler virtuell via hardcoding ausrüsten. An den Zahlen werde ich noch schrauben müssen, da gibt es einige Rädchen an denen man drehen kann, aber fürs erste fühlt es sich ganz gut an (wie gesagt, ich habe keinen Quellcode des Originals, muß mich also auf Beobachtungs-Gabe und Einfallsreichtum verlassen, um ein ähnliches Erlebnis zu re-kreieren).

- Das Kampfsystem in "Kalawaum" ist so simpel wie genial: Die Hitpoints sind zugleich das Maximum an Schaden, die man theoretisch anrichten kann. Wieviel Schaden man macht, hängt von der ausgerüsteten Waffe ab (dahinter verbirgt sich eine prozentuale min/max-Range) - also je mehr Hitpoints ein Charakter verliert, umso näher ist er dem Tode, und umso weniger potentiellen Schaden kann er anrichten, da nützt die beste Waffe nichts. Wenn man herumläuft, regenerieren ALLE Charaktere (Monster wie Spieler) pro Spieler-Schritt einen Lebenspunkt. Das heißt, im Idealfall zwackt man einem Monster via Erstschlag soviele Hitpoints ab, daß es einem im Gegenschlag nicht mehr töten kann. Ansonsten ist taktisches Rumlaufen um Hindernisse angesagt, Monster abhängen indem man durch magische Türen geht, etc. nur um die Hitpoints wieder aufzufüllen. Alternativ kann man Monster auch in Fallen locken, dafür kriegt man jeweils nur 1 Erfahrungspunkt. So richtig XP regnet es nur, wenn man stärkere Monster (= mehr Hitpoints als man selbst) besiegt.

- Die XP-Anzeige gibt das Maximum an Hitpoints an, die man durch Rumlaufen erreichen kann - also es gibt kein Level-Up-System im herkömmlichen Sinne, dennoch wird man mit der Zeit kontinuierlich stärker.
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » Do 13. Aug 2020, 15:08

Clipboard25.jpg

UPDATE:

Die Item-Grafiken sind fertig!
Es gibt, wie im originalen Kalawaum, 6 verschiedene Waffen mit ansteigendem Power-Level. Die Start-Waffe ist die Dämonenpeitsche (so heißt sie auch bei mir). Später kommt noch eine siebte Waffe hinzu, die Fackel, welche die Funktionalität des Zauberstabs im Original übernimmt.
inventoryWeapons.png
Von links nach rechts:
- Die Dämonenpeitsche (Demon Whip)
- Der geschwungene Ritualdolch (Curved Ritual Dagger)
- Das scharf geschliffene Beil (sharp-edged Hatchet)
- Die stattliche Sense (trusty Scythe)
- Der prächtige Krummsäbel (splendid Scimitar)
- Der mächtige Vorlader (mighty Blunderbuss)
- Die fremdartige Strahlenwaffe (alien Energy Coil)
inventoryShields.png
Schilde reduzieren Schaden. In "Kalawaum" gab es 4, bei mir gibt es 5. Von links nach rechts:
- schartiger Lederschild (worn Leather Shield)
- solider Holzschild (sturdy Buckler)
- wehrhafter Knochenschild (jagged Bone Shield)
- glänzender Metallschild (imposing Tower Shield)
- glühender Kometenschild (glowing Comet Shield)

Items haben ein generisches Icon in der Welt-Ansicht; erst wenn man auf das Feld läuft, weiß man genau, um was genau für eine Waffe oder ein Schild es sich handelte. Trägt man bereits ein entsprechendes Item, wird es fallengelassen und das neue Item wird ausgerüstet (Items können zerbrechen im Kampf, das ist einer der nächsten Schritte, die ich programmieren werde).


WEITERE UPDATES:
- Monster fliehen, wenn ihre Hp unter 10% des Maximalwertes sinken. Steigt der Wert wieder über 10%, wechseln sie zurück in den Verfolger-Modus (beim Originalspiel sind sie erst geflohen, wenn Hp < 10, absoluter Wert).
- Jegliche Schaden- und Schadensreduktionswürfe im Kampf werden im Spektrum der Gaußschen Normalverteilung ausgewürfelt, will heißen, es gibt einen Min und einen Max-Wert, und die effektive Spanne wird auf 2 Würfel aufgeteilt. So hat man mehr Konsistenz in der Zufallszahlen-Erzeugung, und bekommt ein besseres Gefühl, welches Item wie stark ist.
- Habe weiter an den Zahlen geschraubt, und nähere mich einem Wert an, der sich richtig gut anfühlt. Habe mich an einen GDC-Talk von einem Blizzard-Designer erinnert, über die "Make it overpowered"-Philosophie des Studios - nur mit drastischen Änderungen (in beiden Richtungen) kommt man dem Zahlenteufel beim Balancing auf der Schliche!
- Die XP-Funktion ist fertig. Man bekommt Erfahrungspunkte abhängig von der Stärke des Monsters, welches man besiegt hat: ein doppelt so starkes Monster (+100% Hp) gibt 8 Erfahrungspunkte, danach scaled der ganze Rest. Also wenn *player\hp = 20 und monster()\hp = 50, erhält man 12 Erfahrungspunkte. Das ist ein schönes, selbst regulierendes System, und existierte genauso im Original (if it ain't broke, don't fix it).

- Zufällige Monster-Verteilung wird dynamisch an maximal zulässige freie Felder angepasst. Die genaue Formel, die ich mir dafür ausgedacht habe, lautet:

Code: Alles auswählen

percent = (Rnd(4,5) * countFree) /100
deviation = (Rnd(4,5) * percent) /100
monsterCount = percent + Rnd(-deviation, deviation)
- Dann habe ich eine erste grobe Scaling-Funktion geschrieben, welche im Laufe des Spiels kontinuierlich die Schwierigkeit erhöht. Der Difficulty-Level richtet sich primär nach den Monster-Stats (Hitpoints), es gibt eine Min-Max-Range, in welcher die Monster (entsprechend ihrem Subtyp) ihre Hitpoints auswürfeln dürfen. Diese Min-Max-Rage wird pro Map erhöht, und zwar als nicht-lineare Kurve:
Der jeweilige Difficulty-Level für eine beliebige Map errechnet sich nach folgender Formel:

Code: Alles auswählen

; Konstanten:
#hitpointsStart = 20 ;Kraftpunkte mit denen der Spieler startet
#dungeonStrengthRange = 100
#dungeonStrengthBaseInc = 10
#dungeonStrengthFactorFloor = 1
#dungeonStrengthFactorCeil = 1.5

dungeonStrengthMin = #hitpointsStart - #dungeonStrengthBaseInc+1
dungeonStrengthMax = dungeonStrengthMin + #dungeonStrengthRange
For a = 0 To currentStage
	dungeonStrengthMin + (#dungeonStrengthFactorFloor * a)
	dungeonStrengthMax + (#dungeonStrengthFactorCei l* a)
Next
Also die erste Map (Index 0) hat eine Difficulty-Range von 21-121
Die zweite Map (Index 1) hat eine Difficulty-Range von 32-132
Die dritte Map (Index 2 hat eine Difficulty-Range von 44-145
Die vierte Map (Index 3) hat eine Difficulty-Range von 57-160
... usw.
So mal als grobe Demonstration, wie so etwas funktionieren könnte!
An den finalen Werten werde ich aber sicher noch schrauben müssen. :geek:

- Und als letzten kleinen Tweak habe ich den Monster Social Distancing beigebracht - wenn eine neue Map aufgebaut wird, muß initial um den Spieler herum ein Sicherheitsradius von 2 Tiles in allen Richtungen (effektiv also 5x5 Felder) eingehalten werden - so wird vermieden, daß Monster direkt neben dem Spieler spawnen.
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
diceman
Beiträge: 8994
Registriert: Mo 7. Mai 2012, 21:21
Kontaktdaten:

Re: Spielereien vom diceman

Beitrag von diceman » So 16. Aug 2020, 16:25

boromirRogue.jpg

Ich weiß, sowas sagt man nicht, aber ich komme dem Ende zu. :shock: :shock: :shifty:
Sicherlich werde ich einige Wochen/Monate hiermit verbringen, aber momentan sieht es gut aus, daß ich bis Ende August etwas Spielbares vorzeigen kann. Die Mechaniken sind fast alle implementiert und funktionieren, die Updates in den letzten Tagen haben dem Projekt endlich den notwendigen Charakter gegeben - es fühlt sich wie ein "Spiel" an!


UPDATE:

-Items (Waffen, Schilde, Schlüssel) können abgelegt werden. Insbesondere das Ablegen von Schlüsseln hatte in "Kalawaum" einen taktischen Nutzen, da man automatisch offene Türen beim Durchschreiten abschließt, sofern man den entsprechenden Schlüssel im Inventar trägt.
Ursprünglich hatte ich streng kontrolliert, welche Felder zulässig für Item-Ablage sind - diese Abfrage-Routinen habe ich nun weitgehend entfernt, weil ich mir gedacht habe: what the heck, lass den Spieler doch Items auf Teleporter ablegen, wenn er mag!
Dazu mußte ich die checkStep()-Logik, welche nach jedem Schritt überprüft, ob ein Event getriggert wurde, umstellen, damit multiple Ereignisse methodisch abgearbeitet werden können, und Fallen weiterhin so tödlich bleiben wie zuvor. Das Ergebnis ist ein super-modales System, bei dem man, wenn man auf einen Teleporter tritt auf dem zugleich ein Item liegt, zunächst das Item aufsammelt, und dann direkt wegteleportiert wird, UND dann kann man noch ein Monster am Zielort aufwecken. Lediglich für das Zielfeld wird kein Check ausgeführt - also wenn dort auch ein Item liegt, muß man sich erst wegbewegen und dann erneut das Feld betreten.
Theoretisch kann einen ein Teleporter mit fixer Zieladresse auf ein Feld mit Monster schicken - das ist aber nicht weiter tragisch, das Programm kommt gut klar damit, und wenn man es einfach "Ambush" nennt, hat man sogar ein Feature anstelle eines Bugs: :P - je nachdem, wer am Zug ist (Monster oder Spieler - letzterer, wenn er das Monster gerade aufgeweckt hat), muß das Feld verlassen, und wird dann wahrscheinlich im nächsten Zug direkt angegriffen. Und da dies eine Ausnahmesituation darstellt, da bei Monster-Erstellung Zieladressen von statischen Teleportern ohnehin aus dem Pool an zulässigen Tiles herausgenommen werden, handelt es sich um einen edge case, dessen Konsequenzen gut tragbar sind.

- Waffen und Schilde können zerbrechen! Hier habe ich einiges getweaked und verschiedene Systeme ausprobiert. Im ersten Draft hatte jedes Item einen durability-Stat, und wenn dieser einen festgelegten Breaking Point erreicht hat, ist das Item zerbrochen. Das funktionierte, aber fühlte sich nicht wirklich gut an. Dadurch bekommt der Item-Verschleiß einen unangenehm deterministischen Charakter, also quasi jedes Item, was man aufsammelt, ist letztendlich zum Zerbrechen verurteilt.
Also back to the drawing board ... in der finalen Routine gibt es jetzt einen Threshhold-Wert, welcher für Waffen variiert, wie stark man damit zugeschlagen hat. Dieser Wert wird durch eine Prozentangabe beschrieben, wo genau in der Range der Waffe sich der gewürfelte Schaden befindet (1-100). Gegen diesen Wert wird mit Random(5000, 1) gewürfelt (5000 ist die Breaking-Point-Konstante), und wenn das Ergebnis kleiner/gleich dem ermittelten Threshhold-Value ist, zerbricht das Item. Also effektiv, je nachdem wie stark zugeschlagen wurde, gibt es eine 0.02 - 2.0 % Chance, daß ein Item zerbricht). Für Schilde existiert derselbe Breaking-Point, allerdings werden hier 2 Threshhold-Values aufaddiert, bevor dagegen gewürfelt wird: wie stark der Gegner zugeschlagen hat, und wieviel % Schaden reduziert wurden. Effektiv haben Schilde also einen etwas höheren Verschleiß. Das fühlt sich richtig gut an, und scheint ähnlich im Originalspiel ähnlich abzulaufen - da konnte auch ein Langschwert beim ersten Schlag zerbrechen ( :evil: ) - ich glaube sogar, daß mein System ETWAS freundlicher ist.

- Türen können mit Gewalt geöffnet werden. Diese Option wird einem angeboten, wenn man versucht, eine Tür zu öffnen und keinen passenden Schlüssel besitzt UND wenn man mindestens eine Waffe und/oder einen Schild ausgerüstet hat (mit der Dämonenpeitsche = Startwaffe kann man keine Türen öffnen). Dazu wird einmal gegen jedes ausgerüstete Item-Wertigkeit gewürfelt (Random(5, 1) für Schilde, und Random(6, 1) für Waffen ), und wenn mindestens einer der beiden Würfe <= der Item-Wertigkeit, wird die Tür erfolgreich aufgebrochen. Anschließend wird für jedes ausgerüstete Item gewürfelt, ob es zerbricht (33%-Chance bei Mißerfolg, 66%-Chance bei Erfolg). Also wenn man Waffe UND Schild ausgerüstet hat, hat man eine höhere Chance auf Erfolg, riskiert aber auch, daß evtl. beide Items kaputt gehen.
Sowieso sollte dies nicht die bevorzugte Art und Weise sein, Türen zu öffnen, aber da im Originalspiel es immer wieder zu unglücklichen Situationen kommen konnte, in denen man sich eingeschlossen hat, habe ich diese Option als letzten Strohhalm eingebaut.

- Bestimmte Events haben atmosphärische Text-Nachrichten bekommen. Je nach ausgewählter Sprache in deutsch oder englisch (kein großer Akt, da ich jeglichen ingame-Text vorab als dimensionierte Strings einlese):

Clipboard26.png

- Da das Spiel über Lovecraft-Thematik verfügt, und somit geistige Gesundheit des Protagonisten eine Rolle spielt, habe ich mir eine "Sanity"-Mechanik ausgedacht - diese ist recht simpel, gibt aber dem Gameplay-Loop mehr Atmosphäre und taktischen Pfiff:
Man startet mit Sanity = 5. Im endgültigen Spiel wird es 10 verschiedene Monster geben, die Hälfte hat ab sofort die Fähigkeit, dem Spieler Geistige Gesundheit zu rauben (Tiefe Wesen, Dunkeldürre, Shogotten, Phantome und Sternengezüchte) ... und zwar nicht durch Angriffe, sondern passiv, alleine dadurch, daß der Spieler am Ende seines Zuges neben einem Monster steht. Dieser Monster-Skill ist Prozent-basiert, gibt mir somit einen größeren Design Space, da sich Monster nun auch durch andere Werte definieren, als nur Kraftpunkte und Schadenspotential. Also wenn man wegläuft vor Monstern um seine Kraftpunkte wieder aufzuladen, muß man nun zusätzlich seine Sanity-Anzeige im Blick haben
Geistige Gesundheit lädt sich wieder auf, wenn man herumläuft, und kein Monster neben sich stehen hat. Zusätzlich gibt jeglicher physische Schaden, der ausgeteilt wird, egal ob durch Spieler oder Monster, +1 Sanity-Punkt zurück. Dies ist ungemein thematisch, da man nun, bei niedriger Sanity + getriggerter Aggro, sich zwischendurch immer Schmerzen zufügen muß, um nicht wahnsinnig zu werden. :shifty:
Der letzte entscheidende taktische Kniff bei dem System: Für jedes im Kampf besiegte Monster (nicht durch Fallen), ganz egal wie stark, erhöht sich die maximale Geistige Gesundheit um +1. Man hat nun ein sinnvolles Entscheidungsgefälle im Umgang mit schwächeren Monstern: cheesed man sie lediglich in eine Falle, kriegt man zwar +1 XP, aber levelt keine Sanity. Andersrum riskiert man natürlich daß Waffe oder Schilde zerbrechen, und man bekommt keine XP, aber man erhöht dauerhaft seine Geistige Gesundheit. Und Sanity ist später, wenn man gefährlicheren Monstern wie Shogotten und Sternengezüchten begegnet, kein zu unterschätzender Stat.

- Items, die im Editor platziert werden, haben ab sofort nur noch eine relative Position. Das Programm liest die Koordinaten aller platzierter Items aus und wendet eine "FloodFill"-Routine auf das entsprechende Tile an, um angrenzende, ebenfalls zulässige Tiles zu erkennen und in einer Liste zu speichern. Ab sofort können Items also in variablen Position gefunden werden - innerhalb der natürlichen Begrenzung ihrer Start-Areale, also kein Item, welches im Editor vor einer Tür platziert wurde (egal ob diese offen oder zu ist), wird sich im Spiel auf einmal dahinter befinden!
Das war eine kleine, aber effektive Maßnahme, welche den Adventure- und Exploration-Fokus des Spiels gehörig aufwertet.
Und auf die "FloodFill"-Routine bin ich besonders stolz:

Clipboard27.png

In Kombination mit dem neuen, eleganteren event-Handling ist nun Potential für tolle emergent gameplay-Situationen gegeben: da initial platzierte Items nun auch auf Teleport-Feldern und Spawn-Traps (welche ebenfalls zufällig verteilt werden) landen können, muß man nun Konsequenzen abwägen, wenn man das daraufliegende Item aufsammeln möchte (tödliche Fallen bleiben natürlich tabu für Item-Placement - so ein Arschloch bin ich dann doch nicht :think: ).

- Last but not least habe ich Funktionalität für einen Screen Shake implementiert. Damit lassen sich prima "Event"-Treffer, also wenn man Monster besiegt, Items kaputt gehen, oder eine Tür aufbricht, visuell hervorheben.
Fühlt sich einfach gut an! :thumbup:
"Ja, Junge, da kann man mal sehen, wie schlecht du denken kannst."
• Jean-Luc Picard

Benutzeravatar
Yuki
Beiträge: 10257
Registriert: Di 8. Mai 2012, 02:21

Re: Spielereien vom diceman

Beitrag von Yuki » Fr 21. Aug 2020, 23:01

Gibt es davon eigentlich dann auch wieder einen Download oder hab ich den nur nicht gesehen xD? Ich würde es ja ausprobieren, sieht ja nach dem "aufwändigsten/vollständigsten" Game bisher aus xD.

Wir haben btw jetzt auch eine neuen Trailer zum 1.0 Release gemacht :D. Aber das ist jetzt eigentlich nur der Anfang für die Arbeit zu Version 2.0 (mal gucken ob wir dafür noch negativ Feedback kriegen). Das Spiel hat übrigens nix mit den Jetzt-Zuständen zu tun, aber die Story nach 2 Jahren nochmal abzuändern, war mir dann auch zu doof lol.

Antworten