mmofacts.com

RPG Kampfscript

gepostet vor 13 Jahre, 10 Monate von BlackScorp

Hallo leute,

ich möchte gerne in meinem RPG ein Kampfscript umsetzen... nur stehe ich gerade auf dem schlauch.. ich habe weis gerade nicht ob es sinnvoll wäre eine tabelle battle zu erstellen mit aktuellen hp/mp des spielers und monster. Habe mir das so gedacht dass ich beim starten eines kampfes eine neue zeile in der tabelle battle erstelle und übertrage in diese tabelle die user id, monster id,hp/mp des users und des monster,zusätzlich speichere ich ein cookie mit der battle id, damit ich, falls der browser abstürzt direkt wieder in den kampf reinkomme und damit die spieler nicht einfach so, wenn du kurz vom sterben sind , abbrechen können.

beim klicken auf angriff button, wird ein ajax request gesendet mit dem skill namen, den ich ausführen will, anhand des skills wird auf der server seite ein damage errechnet und vom monster abgezogen, das ergebnis wird in der datenbank aktualisiert und zurück geschickt an den client... das problem kommt jetzt mit dem monster... ich möchte natürlich auch jedem monster bestimmte skills zuweisen und jedes monster hat unterscheidliche anzahl an skills und wie ich das am besten löse. Meine erste idee ist. eine Tabelle skills zu erstellen mit allen parametern, wie skill name, effekt, kosten usw und in der monster tabelle, wollte ich die monster mehrmals kopieren mit unterschiedlicher skill_id.. später das ganze auch erweitern für items die das monster droppen kann.. nun wird dann eventuell meine tabelle eventuell 30 datensätze pro monster haben.. kommt mir ein bisschen zu viel vor. gibt es denn eine elegante lösung um monstern skills und items zuzuweisen?

Oder ist mein ansatz komplett falsch? wie würdet oder habt ihr ein kampfsystem umgesetzt? jetzt ablauftechnisch.. hoffe ihr könnt mir weiterhelfen

MFG

gepostet vor 13 Jahre, 10 Monate von darwolia

Bei mir läuft das Kampfscript nicht persistent. Also bisher können bei mir die Spieler dieses Schlupfloch ausnutzen, sich auszuloggen, wenn sie kurz vor'm Tod sind. Ob ich das noch ändere, indem ich den aktuellen Kampfstatus (Monster-Infos inklusive) nach jeder Kampfrunde in der DB sichere, überlege ich mir noch.

Skills haben meine Monster nicht DB-seitig, sondern script-seitig - es gibt eine grundlegende Monsterklasse, die normales 0815-Monster-Verhalten abdeckt. Zuschlagen, Rüstung, Sterben, Loot droppen. Die Klassiker halt. Für Monster, die besonderes können sollen - wie beispielsweise Spells casten, Waffen zerstören, ihr Verhalten im Kampfverlauf ändern, muss eine extra-Klasse her, die dann die Monsterklasse extended (das ganze ist btw php-basiert). In der Datenbank ist ein Feld vorgesehen für eine besondere Monsterklasse, die entsprechende php-datei wird dann nach einem Benennungsschema included (mobs_ratte für zu "inc/mobs/ratte.php" mit "class mobs_ratte extends Monsterklasse"). Im Laufe des Kampfes werden diverse Funktionen aufgerufen, die die spezielle klasse dann überschreiben kann (Meine Ratte überschreibt bespielsweise print_mob_hit() für besondere Kampfmeldungen, die_msg() für eine eigene Todesmeldung, drop_loot(), um noch loot ausser der Reihe zu droppen (bei aktiver Quest)). Dabei können unterschiedliche Monster durchaus auch die gleiche Klasse haben - bei jedem Funktionsaufruf auf die Klasse wird die MonsterID mit übertragen.

Das Konzept ist natürlich nur sinnvoll, wenn der Großteil der Monster Standard-Verhalten an den Tag legt. Wenn quasi jedes Monster Spells etc haben soll, ist dieser Weg sehr aufwändig.

Loot läuft bei mir über eine Loot-Table in der DB: Jedes Monster hat ein Feld "loot_id", das auf die Loot-Table verweist. Diese enthält dann eine Zuordnung: Lootid->(itemid, wahrscheinlichkeit, min, max) mit mehreren möglichen Datensätzen pro lootid (für jede Kombination Lootid+mögliches Item 1 Datensatz). Dazu kommen noch kleine Extras wie Referenzen (statt itemid referenz auf andere lootid) und lootgruppen (nur 1 item aus der lootgruppe darf droppen).

Viele Ideen für das Tabellendesign kommen bei mir von WoW - hab früher an nem freeshard mitentwickelt und da ziemlich viel in der Datenbank gebastelt. Da findet man gute Ansätze und Ideen, wie quasi der komplette Spielcontent in einer SQL-DB abgebildet werden kann.

http://udbwiki.webhop.net/index.php/Mangosdb_struct

Auf der Seite findet man die Struktur der grundlegenden Datenbank. Wer Content erstellen will  und das größtenteils in der DB, der wird da sicher einige Anregungen finden.

Da gibt's auch einen guten Ansatz für die Frage nach Monster-Spells rein in der Datenbank - nennt sich EventAI und basiert auf Events und Actions. In der Datenbank werden zu dem Monster dann verschiedene Datensätze hinterlegt, die einem Event (z.B: Lebenpunkte < x, y Zeit vergangen, Start des Kampfes, Tod etc pp) verschiedene Aktionen (Benutze Spell X, fliehe, beschwöre Freund etc pp) zuordnen. unter http://udbwiki.webhop.net/index.php/Event_AI findet sich der Aufbau mit den implementierten Events und Actions.

Im Kampfscript gibt es dann mehrere stellen, an denen gecheckt wird, ob ein Event für die entsprechende Situation vorliegt und dann wird die entsprechende Action ausgeführt.

Wenn man dann noch Spells/Skills in die DB packt (spellid, schaden, schadenstyp, kosten etc pp) und das dann in der "KI-Tabelle" referenziert, erhält man denke ich ein sehr mächtiges System.

gepostet vor 13 Jahre, 10 Monate von BlackScorp

hm... ich habe mir jetzt überlegt eine einzige monster klasse zu schreiben , die je nach id einen anderen array verwendet. habe mir jetzt eine monsterList.php datei angelegt mit so einem array:

$monster['ghost'] = array(
    'attributes' => array(
        'strenght' => 5,
        'dextery' => 2,
        'intelligence' => 2,
        'constitution' => 6,
        'wisdom' => 3
    ),
    'propertys' => array(
        'countFrames' => 8,
        'animation' => 'ghost',
        'name' => 'ghost',
        'description' => 'ghost_desc',
        'spawnDelay' => 60
    ),
    'skills' => array(
        'attack'
    )

also alle informationen die ich über ein monster brauche werden geladen und nun muss ich überlegen was die klasse eigentlich tun soll denke da an methoden wie useSkill(), updateAttributes(),loadAttributes() so dass player eigentlich dann nicht das monster angreift, sondern den HP wert in der datenbank ändert und die monster klasse die Attribute aus der db lädt.. ich werde erstmal damit mich versuchen.. vielen dank für deine tipps.. bin mir sicher ich stoße früher oder später auf weitere probleme;)

MFG

gepostet vor 13 Jahre, 10 Monate von DrakeL

So als Denkanstoss:

Klasse "Monster" das ein Array mit Skills und deren Einstellungen beinhaltet:

PHP:

class FireMonster extends Monster {
private $_skills = array(
'fire' => array('damage' => 10, 'cooldown' => 5, 'priority' => 1);
);
}

Cooldown und Priorität kann dann für eine KI ausgenutzt werden, daher das Monster nutzt immer den Skill der die höchste Priorität hat und derzeit keinen Cooldown. Erweitern kann man dies wenn man noch Bedingungen fürs Auslösen hinzufügt wie einen Heilskill der als Bedingung bekommt dass er erst gecastet wird wenn das Monster <= 50% seines Lebens besitzt.

Dann Skillklassen die die Wirkungsweise der Skills implementieren. Die Klassen selbst können auf verschiedene Events reagieren. Eigene Klassen deshalb weil jeder Skill komplett andere Funktionsweise haben kann aber von vielen Monstern in unterschiedlichen Stärken und Einstellungen genutzt werden kann:

PHP:

class FireSkill extends Skill {
public function onCast($target) {
$target->setHp($target->getHp() - $this->parameters['damage']);
}
}
class ShieldSkill extends Skill {
public function onCast($target) {
$target->addEffekt('Shield', array('shield' => 100, 'duration' => 20));
}
}

In vielen RPGs gibt es darüber hinaus Effekte die über einen gewissen Zeitraum beim Spieler/Monster bestehen bleiben und auf verschiedene Events reagieren:

PHP:

class ShieldEvent extends Event {
public function onGetDamage(&$damage) {
$this->parameters['shield'] -= $damage;
$damage -= $this->parameters['shield'];
}
}

Auf die Art und Weise kannst dir mit ein paar wenigen Klassen für Skills und Effekte viele verschiedene Auswirkungen entwickeln und mit paar Einstellungen in allen möglichen Variationen den jeweiligen Monstern geben und relativ einfach eine gewisse KI aufbauen.

Solche Skripte in die Datenbank schreiben halte ich für sinnlos, denn je nach Komplexität muss/sollte dafür ein Entwickler ran und als Entwickler möchte man wohl nicht über eine Weboberfläche Quellcode schreiben. Denkbar wäre dann eher dass die Skills mit den Einstellungen den Monstern über eine Weboberfläche zugewiesen werden und aus der Datenbank ausgelesen statt fest als Attribut in der jeweiligen Monsterklasse definiert. Das ermöglicht es dass nicht immer ein Entwickler ranmuss bzw. der Quellcode angepasst werden muss wenn es um Balancingänderungen geht.

Wie der Kampf dann abläuft ist eine andere Sache. Die Daten können in einem endlos laufenden Skript gehalten und berechnet werden das alle Events rechtzeitig auslöst und auf Benutzereingaben reagiert oder wenn es zum Beispiel rundenbasierte Kämpfe sind können die Daten in der Datenbank gehalten werden und nach jeder Benutzereingabe eine Kampfrunde berechnet werden.

Was die KI angeht kann man sich zum Beispiel sehr gut in Final Fantasy 12 anschauen. Da konnte man Profile mit Bedingungen und deren Auswirkungen für seine Charaktere erstellen und diese haben mehr oder weniger dann automatisch Gekämpft. Fand ich sehr interessant da es das erste Spiel war wo ich so etwas gesehen hatte und etwas mehr Einblick gegeben hat wie eine KI auf Gegnerseite implementiert werden kann.

gepostet vor 13 Jahre, 10 Monate von BlackScorp

Hm.. jetzt hast du mich auf die Blöde idee gebracht, mein Klassenkonzept zu ändern.. zz habe ich klassen die nichts von einander erben sondern klassen rufen intern andere klassen auf usw.. ich glaube ich muss tatsächlich ein wenig ein neues konzept entwickeln. also wiedermal Refactoring.....

gepostet vor 13 Jahre, 10 Monate von DrakeL

Original von BlackScorp

Hm.. jetzt hast du mich auf die Blöde idee gebracht, mein Klassenkonzept zu ändern.. zz habe ich klassen die nichts von einander erben sondern klassen rufen intern andere klassen auf usw.. ich glaube ich muss tatsächlich ein wenig ein neues konzept entwickeln. also wiedermal Refactoring.....

Jedes Monster hat HP und Methoden diese zu verändern (und bei 0 HP auch zu sterben). Jeder Skill hat eine Castzeit, einen Cooldown und Methoden die dies abgreifen müssen. Da würde ich zu Basisklassen greifen die diese Standardverhalten implementieren und an den nötigen Stellen Eventmethoden aufrufen wo jeweilige abgeleitete Skillklassen dann eingreifen können, ja.

Du kannst natürlich auch eine Skillklasse machen und Methoden/Attribute wo die Verhalten zuweisen kannst, das wäre sogar geschickter da auch mehrere Verhalten miteinander kompinieren kannst. Zum Beispiel:

PHP:

class Skill {
public $skills = array();
}
$fireshield = new Skill();
$fireshield['skills'][] = new FireSkill();
$fireshield['skills'][] = new ShieldSkill();

Durch die Verschachtelung wäre es sehr gut möglich zum Beispiel einen Skill zu machen der die ganze Gruppe betrifft und dann entweder nen Schadensskill reinzupacken, dann hast sofort AoE Damage oder einen Heilskill einzupacken und hast einen AoE Heal. Oder einen Skill der tickt (kanalisierende Zauber) und da dann den Gruppenskill mit Schadenskill reinpacken und schon hast einen sehr komplexen Schadenszauber der über die Zeit der Gegnergruppe Schaden zufügt mit sehr einfachen Mitteln und durch eine andere Verschachtelung hättest dann einen sehr komplexen Heilzauber der über die Zeit die Gruppe heilt.

gepostet vor 13 Jahre, 10 Monate von BlackScorp

An sowas kompliziertes habe ich garnicht gedacht.. ich dachte mir folgendes:

PHP:

class Enemy{
public function __construct($name){
$this->name = $name;
}

public function fight(){
switch($this->hp){
case '10':{
$this->useSkill('xyz'); // in datenbank hp wert vom user ändern
break;
}
}
}

private function loadAvaliableSkills(){
//include datei mit monster array
}
}


class Battle{
public function fight(){
if(isset($_GET['fight'])){
$enemy->fight();
$player->useSkill($_GET['skill']); // ändere wert in der db
}
}

public function __construct(){
$enemy = new Enemy('xyz'); //
$player = new Player($_SESSION['playerID']);
}
}

und dann mit ajaxBattle.php?attack=monsterID&skill=roundhousekick die battle klasse instanzieren und fighten lassen...

gepostet vor 13 Jahre, 10 Monate von DrakeL

PHP:

public function fight(){
switch($this->hp){
case '10':{
$this->useSkill('xyz'); // in datenbank hp wert vom user ändern
break;
}
}
}

Das sieht sehr unflexible aus. Du hast nur eine Art von Bedingung (HP Abhängigkeit). Das würde ich nicht in eine solche strenge Struktur (einem switch) packen, sondern aufweichen dass verschiedene Abhängigkeiten mit verschiedenen Werten usw. definieren kannst und diese Bedingungen auch mischen kannst.

Sowas würde ich aber nicht in Quellcode packen, sondern in eine Einstellungsdatei/Datenbank spätestens wenn mal jemand hast der Balancing macht. Dieser sollte die Bedingungen selbst schnell ändern können um verschiedene Sachen testen zu können.

Aber ist ja dann vom Projekt abhängig, wenn alleine am Projekt arbeitest lässt sich da besitmmt was wegkürzen. Wobei ich eher so eingestellt bin dass ich die Struktur auch wenn ich alleine bin trotzdem so flexible wie möglich aufbaue.

Original von BlackScorp

An sowas kompliziertes habe ich garnicht gedacht.. ich dachte mir folgendes:

Das System wie ich beschrieben habe ist nichtmal sehr kompliziert. Klar man muss sich genau Gedanken machen darüber in welche Klassen man welche Funktionalitäten verteilt sodass die Struktur am Ende auch genauso flexible funktioniert. Dafür hast dann aber auch ein System wo mit einfachen Mitteln sehr schnell viele Möglichkeiten hast.

Ich würde es als sehr gefährlich ansehen da eine schnelle einfache Lösung zu implementieren weil dann bei jeder Änderung wieder viel Aufwand notwendig ist. Wobei das auch wieder vom Spiel abhängt in welcher Größenordnung es sich bewegt.

Daher komplex = mehr Anfangsaufwand dafür danach sehr schnelle Entwicklung und Erweiterung

Oder Schnellschuss = Schnelles Ergebnis, kaum Erweiterbar

gepostet vor 13 Jahre, 10 Monate von BlackScorp

Daher komplex = mehr Anfangsaufwand dafür danach sehr schnelle Entwicklung und Erweiterung

Oder Schnellschuss = Schnelles Ergebnis, kaum Erweiterbar

hast schon recht.. ich sehe aber keinen sinn für jeden skill eine eigene klasse anzulegen.. ich denke es wäre besser eine skill klasse zu haben, die sich benötigten daten aus einem array lädt und dann nach den daten halt entscheided welche funktion wann aufgerufen wird und welche effekt verursacht die.

und wegen dem switch case... das war jetzt einfach mal ein pseudocode wollte halt sagen dass je nach einer bestimmten bedingung , ein bestimmtes erreignis ausgelößt werden soll..

gepostet vor 13 Jahre, 10 Monate von DrakeL

Original von BlackScorp

hast schon recht.. ich sehe aber keinen sinn für jeden skill eine eigene klasse anzulegen.. ich denke es wäre besser eine skill klasse zu haben, die sich benötigten daten aus einem array lädt und dann nach den daten halt entscheided welche funktion wann aufgerufen wird und welche effekt verursacht die.

Dein Vorschlag:

Eine Klasse die alle Skillverhalten (Schaden, Heilung, Effekt anbringen, Gruppenzauber, Kanalisierend) beinhaltet. Daher eine Datei mit vielleicht 1000 Zeilen die alles kann. Neue Verhalten würde Änderungen an vielleicht vielen Stellen in der Klasse bedeuten, daher schlecht wartbar und fehleranfällig.

Mein Vorschlag:

Für jede Art von Verhalten eine Klasse die streng abgetrennt genau dieses Verhalten implementiert. Neue Verhalten einfach neue Datei mit neuer Klasse und X neue Variationen sind möglich. Alle Klassen beschränken sich auf wenige dutzend Zeilen.

(Als Beispiel wie es bei mir war: Schadenszauber und Heilzauber sind verfügbar. Jetzt wollen die Gamedesigner aber einen Schadenszauber der auf Gruppen geht. Also neue Datei mit einem Verhalten wo alle verschachtelten Zauber auf alle Gegner ausgeführt werden, waren paar Zeilen Code. Danach kamen sie auf die Idee wir brauchen auch Gruppenheilzauber: Ja fertig stellt in euren Daten einfach ein dass der Heilzauber im Grupepnzauber verschachtelt ist, da muss nichts programmiert werden).

Also ehrlich gesagt ich sehe absolut keinen Sinn darin eine Klasse zu machen die alles kann. Dir würden damit alle Vorteile der Objektorientierung verloren gehen. Im besten Fall hätte man bei deinem Vorschlag für jedes Verhalten eine Methode die es klar trennt. Dann bist auch nicht mehr weit entfernt von meinem Vorschlag.

Und die Daten haben erstmal nichts in den Klassen zu suchen. Die Klassen sollen die Verhalten implementieren wie man Schaden zufügt, also was Schaden ist, welche Werte dabei geändert werden müssen usw. Die Daten wie viel Schaden ein Skill macht, wie viel Mana der Skill kostet, wie lange der Cooldown dann ist haben in diesen Klassen nichts verloren und gehören dann an eine völlig andere Stelle.

Ist vielleicht etwas schlecht rübergekommen mein Vorschlag, also ich rate dir davon ab für jeden Skill eine Klasse zu machen (wie bei mir FireSkill) sondern für jedes Verhalten eine Klasse, also DamageSkill, EffektSkill, GroupSkill, CanalizeSkill, HealSkill werden wohl die Hauptverhalten sein. Und die 5 verschiedenen Verhalten haben nichts miteinander zu tun und sollten daher auch nicht in einer gemeinsamen Klasse zu finden sein. Darüber dann eine gemeinsame Basisklasse Skill für Sachen wie Manakosten, Cooldown und die Verschachtelung der Skills ineinander.

Wären am Ende 6 Klassen und du hättest damit schon alle Möglichkeiten von Skills wie sie in den meisten RPGs zu finden sind.

und wegen dem switch case... das war jetzt einfach mal ein pseudocode wollte halt sagen dass je nach einer bestimmten bedingung , ein bestimmtes erreignis ausgelößt werden soll..

Ja EINE bestimmte Bedingung auf EIN Ereignis. Das ist eine komkrete Implementierung und das gehört eher zu den Daten. Die Klassen sollen das Verhalten aufzeigen dass es Bedingungen gibt und dass es Ereignisse gibt. Welche Bedingungen und Ereignisse beim jeweiligen konkreten Skill ausgelöst werden sind Balancingdaten und die würde ich sehr strikt vom Quellcode fernhalten.

gepostet vor 13 Jahre, 10 Monate von Dhyani

Um mal ein Beispiel einer "Konfigurations"-Datei zu zeigen:

    WEAPON MissileLauncher IS
       
        USERNAME        :    "Raketenwerfer"
        ACRONYM            :    MSLN
        INFORMATION                    :
        {$
           
               
                    Hilfetext
               
               
                    Text
               
           
        $}
        REQUIRES
            MissileTech[Stufe]
        SHOTS            :    Wert
        ACCURACY      :    Wert * Bonus1 * Bonus2 * Bonusn
        RANGE            :    Wert * Bonus1 * Bonus2 * Bonusn
        DAMAGE          :    Wert * Bonus1 * Bonus2 * Bonusn
        ARMOREFFECT        :    Wert
        SHIELDEFFECT    :    Wert
    END    MissileLauncher;

Das sind alle Eigenschaften einer bestimmten Waffe inclusive Berechnungsformeln fuer Boni auf bestimmte Eigenschaften. Muss aus Balance-Gruenden etwas geaendert werden, bedarf es ausschliesslich einer Aenderung an dieser Config-Datei.

Weiterhin liegt ein Vorteil, dass ein Objekt mit obigen Eigenschaften Teil anderer Objekte werden kann. Zum Beispiel einer Verteidiungsanlage, eines Raumschiffes oder was auch immer. Und das garantiert hoechste Flexibilitaet.

Dhyani

gepostet vor 13 Jahre, 9 Monate von dewarim

Original von BlackScorp

Meine erste idee ist. eine Tabelle skills zu erstellen mit allen parametern, wie skill name, effekt, kosten usw und in der monster tabelle, wollte ich die monster mehrmals kopieren mit unterschiedlicher skill_id.. später das ganze auch erweitern für items die das monster droppen kann.. nun wird dann eventuell meine tabelle eventuell 30 datensätze pro monster haben.. kommt mir ein bisschen zu viel vor. gibt es denn eine elegante lösung um monstern skills und items zuzuweisen?

(um nochmal zum Anfang des Threads zurückzukehren ;) )

In LittleGoblin habe ich das zur Zeit so gelöst:

Ein konkretes Monster hat einen Typ (MobTemplate), aus dessen Werten es initialisiert wird. Dabei werden (fast) nur die veränderlichen Werte im Monster gespeichert, der Rest wird bei Bedarf aus dem Template geholt.

Wenn es also 100 Orks gibt, die mit Charakteren kämpfen, dann hat zwar jeder Ork seine elementaren Daten im eigenen Objekt (hit points, damage, ...), einfach weil sich diese ja im Laufe des Kampfes ändern könnten. Die Kampfattribute (Monster verursacht Feuerschaden oder Kälteschaden ...) aber werden im Template gehalten bzw. sind 1..n mit dem MobTemplate verbunden.

D.h.: "Monster hat Typ hat Kampfattribute" anstelle von "Monster beinhaltet Kampfattribute"

Analog gilt das auch für Items und andere Klassen: Dinge, die sich viele Objekte teilen, lassen sich gut in Type/Template-Klassen auslagern. Das macht dann auch die Tabellen in der Datenbank übersichtlicher, wenn eine Waffe nicht 30 Spalten für Schadenstypen hat (Sword of Death, Chaos and Bread slicing) , sondern sein Typ n..m mit einer Tabelle der Schadenstypen verbunden ist. (Oder 1..n mit einer Join-Table itemtype_damagetype, in der dann steht, wie viel Schaden die Waffe durch Feuer macht).

Auf diese Diskussion antworten