mmofacts.com

Aufgabensystem a.k.a. Questsystem

gepostet vor 15 Jahre, 7 Monate von KoMtuR

Ich mach mir gerade ein Plan, wie ich so ein Modul implementieren kann und stecke irgendwie in der Sackgasse. Man kennt es ja von einigen Spielen. Sei es nun ein Tutorial oder einfache Aufgaben, die man lösen muss, bis hin zum komplexen Questsystem in Rollenspielen.

Der User muss immer eine Abfolge von Klicks machen oder irgendein Ziel erreichen, bis diese Aufgabe im System als erledigt abgehakt wird. Nur die Frage ist, wie setzt man das gescheit um?

Da ich immer ein wenig überheblich werde will ich es ja auch so flexibel wie möglich machen. Also Nutzer A klickt auf den Button und schon könnte eine Aufgabe gelöst sein. Oder Nutzer B bekommt ein virtuelles Klavier vorgesetzt und muss "Alle meine Enchen" nachspielen, um die Aufgabe zu lösen.

Nur leider fehlt mir derzeit der Lösungsansatz Es gibt ja sicherlich paar Leute, die das schonmal umgesetzt haben. Ihr müsst ja nicht alles im Detail erklären Ich wollte aber auch nicht anfangen irgendwo was hart reinzucodieren. Irgendeine sinnvolle Lösungsmethode muss es ja geben.
Hatte die letzten Tage ein wenig Xhodon angespielt und die hatten eigentlich ein schönes System. Man konnte nicht irgendein Muster erkennen, wie in WoW zb.
Da gibts ja nur x Haupttypen und dann wird da halt Text und Belohnung eingesetzt

Ich hatte mir erst überlegt, dass ich programmintern eine Art Ereignis-Verzeichnis anlege. So ne Art Bayeux-Verfahren. Man hat Kanäle und da können die Interessierten unterschreiben und bekommen dann die neusten Ereignisse. nur irgendwie finde ich das auch nicht das Wahre. Wird ja ziemlich viel Müll hin und her bewegt, den man dann eigentlich garnicht brauch, sonbdern nur zu bestimmten Fällen.

Wäre gut, wenn paar Ideen kommen würden (brauch kein Code sein, sondern halt nur eigene Erfahrung, Anregungen, psychische Ergüsse usw.)

KoMtuR

gepostet vor 15 Jahre, 7 Monate von Fobby

Ich hatte in meinem Browser-RPG ein WoW-ähnliches Questsystem. Annehmen beim NPC und dann: Items finden, Monster töten, NPC finden. Das habe ich zwar in einer kleinen Klasse verwaltet aber ich habe hardcoded bei jedem entsprechenden Event (Kampf zuende vor allen -> Items droppen, Monster sterben) die aktiven Questziele der betreffenden User ausgelesen und behandelt (Monstercounter erhöht, Questitem gedroppt ... sowas).

Nicht wahnsinnig flexibel und schon gar nicht abwechslungsreich - aber es hat funktioniert :)

Variabel war übrigens, was bei Questbeginn und -ende passiert. Da habe ich es mir offengelassen, dort ein File einzubinden anstatt eines schönden NPC-Textes. Damit war dann natürlich sehr viel möglich - aber schon sehr hart gecodet.

gepostet vor 15 Jahre, 7 Monate von KoMtuR

Hmm stimmt bei Tötungsaufgaben (Quests ziehen das immer so ins RPG-mäßige) hilft kein Eventsystem, da am Loot was geändert werden müsste. Das mit den Tötungen gänge ja vielleicht noch darüber zu lösen.
Und du hast dann sicherlich auch eine Tabelle gehabt, wo du alle erledigten Quests drinne hattest, richtig? Sozusagen immer nachgeschaut, ob der Typ von NPC x überhaupt Quests annehmen kann, oder ob dr die schon abgeschlossen hat.

Schwieriger wirds dann schon, wenn man das noch ein wenig weiter spinnt und man eine Karte hat, wo die NPCs auch sichtbare Quests haben (es müssen ja nicht unbedingt Ausrufezeichen sein )

Sicherlich wird es bei mir auch auf das Schema "abstrakte Grundklasse mit Einstellungsmöglichkeiten". Zumindest wird daran kein Weg vorbeiführen. Ok das mit den Monstern wäre einfach ausgedrückt:

Questlog.hasQuestCondition(monsterid) -> true -> LootList.enableQuestLoot(questid)

oder sowas :D

Naja noch schwieriger wäre es dann, wenn man von Monstern weg geht und bestimmte Rohstoffe herstellen soll (um mal jedes Genre abzuklappern). Man kann ja shclecht prüfen: if(rohstoff >= geforderteMenge) weil man das ja herstellt und im gleichen Moment auch wieder verbrauchen könnte. Oder ich verstehs einfach nur falsch

gepostet vor 15 Jahre, 7 Monate von altertoby

Also irgendwie kannst du m.E.n jedes Questsystem auf wiederkehrende Quest zurückführen. Dann hättest du je nach Quest eine Quest-Klasse und x erbende "Special"-Quest (sowas wie TöteMonster, StelleRohstoffe her,...).

Nen einfacher Quest müsste wohl sowas wie "StartQuest()", "GetCurrentState()", "DisplayWinMessage()", evt noch ein Quest-spezifirsches "UpdateProgress(xyz)" haben. In StartQuest kannst du dir dann auch den Anfangszustand merken (z.B. Rohstoffe zu Beginn, oder getötete Monster bis Dato) und DisplayWinMessage könnte auf ein breites Spektrum von verschiedenen Texten zurückgreifen und so die Quest-Klassen etwas verschleiern. 

Zu dem Problem mit der Tötungsaufgabe: Dort würde einfach nach Ende des Kampfes die Liste aller aktiven Quests des Spielers geladen, dort dann für alle in Frage kommenden Quests (evt. über Interfaces gelöst) die Methode "UpdateProgress" aufgerufen, in der dann das getöte Monster extra gespeichert wird (oder ein Special-Item gedroppt).

Hättest du zwar immernoch eine relativ Hard-Codierte Version, aber wenigstens alles an einem Punkt und auch relativ gut strukturiert.

gepostet vor 15 Jahre, 6 Monate von dreaddy

Die meisten Quests lassen sich einfach anhand abfragbarer Daten realisieren, die werden dann, immer wenn der Spieler seine Belohnung abholen will überprüft. Je mehr Statistiken usw sowieso gespeichert werden für einen Spieler(wie viele Monster welchen Typs habe ich getötet, wie viel Gold insgesamt verdient, welche Nachrichten habe ich schon gelesen etc), desto besser ist das realisierbar.

Wenn es um Dinge geht wie "les den Eintrag zu Diesteln", "schau dir deine Karte an und kehre dann zurück" oder Dinge geht, die einfach nicht gespeichert werden, kannst du das je nach Menge entweder fies reinhacken(für eine ausgefallenere Quest programmiert keiner ein Questsystem ;) ) oder vernünftig lösen.

Eine vernünftige Lösung wäre etwa, wenn die Quest dem System mitteilt "Ich möchte ab sofort bestimmte Ereignisse mitgeteilt bekommen". Beispiele wären da Seitenaufrufe, Monsterkills oder gefundenen Items. Diese werden dann alle weitergeleitet, die Quest wertet sie aus, setzt ggf das flag "erfüllt" und meldet sich wieder beim System ab um dieses nicht unnötig zu belasten.

Hätte den Vorteil, dass Questaufgaben wirklich nur in der jeweiligen Quest abgehandelt werden und sonst nirgends und Sachen wie "töte erst einen Lemming, dann ein Krümelmonster und rufe dann deinen Kampfbericht drei mal auf" wären problemlos und sehr einfach möglich.

gepostet vor 15 Jahre, 6 Monate von KoMtuR

Original von dreaddy

Eine vernünftige Lösung wäre etwa, wenn die Quest dem System mitteilt "Ich möchte ab sofort bestimmte Ereignisse mitgeteilt bekommen". Beispiele wären da Seitenaufrufe, Monsterkills oder gefundenen Items. Diese werden dann alle weitergeleitet, die Quest wertet sie aus, setzt ggf das flag "erfüllt" und meldet sich wieder beim System ab um dieses nicht unnötig zu belasten.

Hätte den Vorteil, dass Questaufgaben wirklich nur in der jeweiligen Quest abgehandelt werden und sonst nirgends und Sachen wie "töte erst einen Lemming, dann ein Krümelmonster und rufe dann deinen Kampfbericht drei mal auf" wären problemlos und sehr einfach möglich.

Genau so hab ich mir das auch überlegt. Ich hatte geplant, dass ich einen Messagebus im Backend habe, wo diverse Module einfach ihren Status oder ihre Aktion mitteilen können. Dies hat erstens den Grund, dass ich alles loggen kann, was ich für nötig halte, und zweitens können zusätzliche Neuerungen bequem auf Daten zugreifen, die sie benötigen, um so ihre Ansicht zu aktualisieren. Dort wollte ich dann halt bestimmte Typen von MessageListener für Quests erstellen, welche bestimmte Typen von Quests für einen User bearbeiten. Somit kann ich das Questsystem beliebig um irgendwelche Questtypen erweitern und aus den reinen Typen, neue vermischte Typen erstellen. Sozusagen wissen die einzelnen Module nicht, wer denn überhaupt der Empfänger ihrer Nachricht ist, weils sie auch nicht interessiert, sondern schickt einfach dumm seine Nachricht "Ich habe das und das gemacht. Nur das ihrs wisst". Wie performant das dann letzendlich ist muss ich noch testen.

Vielleicht muss ich das System noch ein wenig umstellen, weils derzeit ein Bus für alle Spieler gibt und ich dann jedes mal testen muss, ob er eine spezifische Questart hat, obwohl das eigentlich nicht interessiert.

gepostet vor 15 Jahre, 6 Monate von abuzeus

Schaut euch mal das Beobachetermuster im Entwurfsmusterkatalog an. Das wäre eine saubere Implementierung des von dir beschriebenen Verhaltens.

gepostet vor 15 Jahre, 6 Monate von buhrmi

In Dunwich ist das Questsystem über method aliasing realisiert. Evtl. gibt es etwas ähnliches auch in PHP. Erkundige dich mal in diese Richtung, womöglich kommst du dann auf weitere Ideen zur Lösung.

Method Aliasing bedeutet im Grunde dass bestimmte Codestücke vor oder nach Methodenaufrufen auf Klassen drangesetzt werden und ausgeführt werden. Beispielsweise aliased eine Quest, welche auf ein Level-Up des Charakters wartet, die Methode die bei einem Levelup aufgerufen wird, und aktualisiert sich selbst.

Man hat also kein Model-Observer-Pattern, eher eine Art Decorator Pattern.

gepostet vor 15 Jahre, 6 Monate von meikel

Original von buhrmi

Method Aliasing bedeutet im Grunde dass bestimmte Codestücke vor oder nach Methodenaufrufen auf Klassen drangesetzt werden und ausgeführt werden. Beispielsweise aliased eine Quest, welche auf ein Level-Up des Charakters wartet, die Methode die bei einem Levelup aufgerufen wird, und aktualisiert sich selbst.

Erinnert mich irgendwie an einen (SQL) Trigger...

gepostet vor 15 Jahre, 6 Monate von TheUndeadable

> Erinnert mich irgendwie an einen (SQL) Trigger...

Willst du nun hunderte/tausende Trigger über die gesamte Datenbank verteilen, die bei den unterschiedlichsten Questtypen, angefangen von einem einfachen Level-Up und beendet bei einem 'Töte 20 Gnolle' verteilen?

Ich denke nicht, dass dies eine Datenbank performant und sinnvoll erledigen kann. Es ist nicht Aufgabe der Datenbank.

Auch ist der Rückaufruf des Backend-Codes (geschrieben meist in PHP/Java) nur sehr schwer möglich, es sei denn man setzt einen MS SQL Server oder PostgreSQL ein (bei ersterem kann man beliebigen .Net-Code triggern, bei letzterem nach meinem Wissen beliebigen Python-Code)

gepostet vor 15 Jahre, 6 Monate von exe

Original von TheUndeadable

> Erinnert mich irgendwie an einen (SQL) Trigger...

[...]

Auch ist der Rückaufruf des Backend-Codes (geschrieben meist in PHP/Java) nur sehr schwer möglich, es sei denn man setzt einen MS SQL Server oder PostgreSQL ein (bei ersterem kann man beliebigen .Net-Code triggern, bei letzterem nach meinem Wissen beliebigen Python-Code)

Python/Perl/PHP/Java/C/SQL. Ist aber nicht unbedingt praktikabel was anderes als C oder SQL für Trigger zu verwenden, da hierbei der entsprechende Interpreter in jeden PostgreSQL-Prozess (jede Verbindung ist in Postgres ein eigener Prozess) geladen werden muss. Was arbeitsspeichertechnisch ziemlich unschön werden kann. Gibt zwar teils auch Möglichkeiten einen Interpreter über mehrere Verbindungen zu sharen, allerdings macht das die Kommunikation des Interpreters zu Postgres auch nicht unbedingt performanter.

gepostet vor 15 Jahre, 6 Monate von meikel

Original von TheUndeadable

> Erinnert mich irgendwie an einen (SQL) Trigger...

Willst du nun hunderte/tausende Trigger ...?

Nein, ich bin schreibfaul.

Prinzipiell isses doch so, daß bei üblichen MM-Brosergames der Kram irgend wie gespeichert wird. Und falls das ein DBMS sein sollte, könnte man ja mal über einen Trigger nachdenken, wenn vor oder nach dem UPDATE noch weitere SQL Operationen erfolgen sollen. Auch ein CURSOR u/o eine nested set Struktur wären ggf. eine Überlegung wert.

Auch wenn MySQL noch kein "industrietaugliches" DBMS ist, aber Trigger, Cursor & Co. sind seit 5.0.x benutzbar.

gepostet vor 15 Jahre, 6 Monate von buhrmi

Anwendungslogik in die Datenbank auszulagern halte ich jetz mal absolut nicht für einen empfehlenswerten Ansatz.

Auf diese Diskussion antworten