Start Info Community Spielen
 
 

Morgengrauner Dokumentation

Dateipfad: /home/mud/mudlib/doc//wiz/todesfallen

Manpage zu Todesfallen. Technischer Leitfaden.


Diese Manpage richtet sich an (Jung)magier, die Todesfallen nutzen wollen,
um verschiedenste Dinge in ihren Gebieten damit zu erreichen. Dabei soll hier
nicht diskutiert werden, wo welche Art von Todesfalle Sinn macht (oder auch
nicht), sondern es sollen technische Aspekte bei der Programmierung einer
solchen (Todes)falle aufgezeigt werden.


Kapitel 1. Das Umfeld einer Todesfalle.

Eine Todesfalle kann wie in spaeteren Kapiteln beschrieben, leicht umgesetzt
werden. Damit sie sich auch so verhaelt wie sie soll, werden hier einige
Sicherheitsaspekte behandelt, die durchgegangen werden sollten.


Kapitel 1.1 Wer hat die Todesfalle ausgeloest?

Die meisten Todesfallen werden durch ein Kommando ausgeloest, welches der
Spieler ausfuehrt. Z.B.
	druecke grossen roten knopf mit der aufschrift "todesfalle ausloesen"
In diesem Fall existiert in der aufgerufenen Funktion die Moeglichkeit, ueber
die Funktion this_player() den aktuell "gueltigen" Spieler auszufischen.
Hierbei ist zu beachten, dass auch NPCs von dieser Funktion zurueckgegeben
werden koennen. Auch im init() eines Raumes wird in this_player() der Spieler
zurueckgegeben. Hierbei ist jedoch zu beachten, dass man immer schauen sollte,
ob this_player() auch tatsaechlich ein Objekt zurueckliefert. Dies ist
wichtig, da das init() (oder auch andere Funktionen) durch Gegenstaende, wie
den Bumerang ausgeloest werden koennen. In diesem Fall steht in this_player()
ueblicherweise kein Objekt. Die Sicherheitsabfrage sollte also wie folgt
aussehen:

    object spieler;
    spieler = this_player();
    if (spieler) // Ein Lebewesen?
    {
      ...
    }
    // else: Die Falle nicht ausloesen, da kein Lebewesen.


Kapitel 1.2 Wer soll die Todesfalle nicht ausloesen?

Oft kommt es vor, dass eine schoene Todesfalle von Gott und der Welt
ausgeloest wird. Normalerweise moechte man aber nicht, dass "Gott" oder andere
besondere Personengruppen diese Falle ausloesen. In den folgenden
Unterkapiteln werden Abfragen gezeigt, um Personengruppen auszuschliessen.


Kapitel 1.2.1 Todesfallen, die nicht auf Geister reagieren.

Ueblicherweise sollen Todesfallen bei den Ausloesern Schaden verursachen. Bei
Geistern laeuft dies jedoch ins Leere. Man koennte nun auf die Idee kommen,
alternativ auf Geister zu reagieren.


Kapitel 1.2.1.1 Nicht auf Geister reagieren.

Angenommen, man hat einen Spieler (siehe oben), der im Begriff ist, die Falle
auszuloesen. Man sollte nun pruefen, ob er ein Geist ist. Falls er kein Geist
ist, kann man wie gewuenscht (Todesfalle) reagieren. Falls nicht, sollte man
eine passende Meldung ausgeben. Z.B. "Als Geist hast Du nicht die notwendige
Kraft, diesen Knopf zu druecken." Die technische Abfrage, ob ein Spieler ein
Geist ist, sieht wie folgt aus.

    if (!spieler->QueryProp(P_GHOST))
    {
        // ist _kein_ Geist, also Todesfalle.
    }
    else
    {
        // Alternativer Text, da Geist.
    }


Kapitel 1.2.1.2 Geister entgeistern.

Da ausschliesslich die Rassenstartraeume entgeistern sollen, wurde dieser
Punkt herausgenommen. Also: Es ist unerwuenscht, Geister zu entgeistern, um
eine Todesfalle korrekt auszuloesen.


Kapitel 1.2.1.3 Alternative fuer Geister.

Rollenspieltechnisch gesehen macht es in den allerwenigsten Faellen Sinn,
alternativ auf einen Geist zu reagieren. Der Vollstaendigkeit halber sei diese
Loesung trotzdem erwaehnt. Ein Programmbeispiel sollte ohne grosse Muehe durch
die schon vorhandenen Beispiele erstellt werden koennen.


Kapitel 1.2.2 Gaeste und kleine Spieler.

Rollenspieltechnisch macht es oft keinen Sinn, auf Gaeste oder kleine Spieler
anders zu reagieren, als auf "den Rest". Moechte man jedoch einen besonderen
Schutz fuer kleine Spieler einbauen ("Du hast zu grosse Angst, diesen grossen,
roten Knopf zu druecken.") oder moechte man Gast-Sterbe-Testern den Hahn 
zudrehen (die Weicheier!), so sind folgende Abfragen unter Umstaenden
sinnvoll.

  // Gast?
  if (spieler->QueryGuest())
  {
      // Ist ein Gast...
  }
  else
  {
      // Todesfalle. Kein Gast.
  }
  
  // Kleiner Spieler?
  // In diesem Beispiel Abfrage des Spielerlevels.
  if (spieler->QueryProp(P_LEVEL) < 10)
  {
      // Spielerlevel < 10 haben zuviel Angst.
  }
  else
  {
      // Spielerlevel >= 10 koennen hier ruhig die Todesfalle ausloesen.
  }


Kapitel 1.2.3 Magier

Ueblicherweise sind Magier unsterblich und soll(t)en auch nicht
(versehentlich) Todesfallen ausloesen. Dies ist vor allem dann der Fall, wenn
"zufaellig" im gleichen Raum stehende Spieler Meldungen bekommen wuerden und
dadurch gewarnt werden. Die technische Umsetzung, dass Magier Todesfallen
nicht ausloesen, sieht wie folgt aus. Die Abfrage beruecksichtigt
"mschau aus". Das heisst, Magier koennen sterben, wenn sie mschau aus gemacht
haben.

    #include 

    if(IS_LEARNER(spieler) && spieler->QueryProp(P_WANTS_TO_LEARN))
    {
      // Magier: Man soll ihm mitteilen, dass er diese Aktion als Magier nicht
      // tun darf.
    }
    else
    {
      // Kein Magier. => Todesfalle.
    }


Kapitel 1.3 Wichtige Punkte einer nicht sinnfreien Todesfalle.

Eine Todesfalle ist, wie in Kapitel 2 gezeigt, eigentlich eine einfache Sache.
Kombiniert mit den schon gezeitgten Sicherheitsabfragen hat man ueblicherweise
ein gueltiges Opfer. Allgemeine Dinge, wie beispielsweise "Das Opfer sollte
ein environment haben" werden hier uebergangen und nur die wesentlichen Dinge
genannt, die beim potentiellen Toeten eines Spielers von Belang sind.


Kapitel 1.3.1 Wo ist der Spieler?

Ganz wichtig bei Todesfallen ist die Frage, wo sich der Spieler befindet.
Wird eine Todesfalle in init() ausgeloest, dann befindet sich der Spieler
schon in dem entsprechenden Raum, dessen init() nun ausgefuehrt wird. Das
bedeutet, wenn der Spieler stirbt, sind seine Sachen zunaechst einmal in dem
betreffenden Raum. In seltenen Faellen spielt es wirklich eine Rolle, wo sich
der Spieler befindet und er hat eigentlich immer ein environment, in das seine
Leiche dann kommt. Dennoch sollte man bei Todesfallen testen, wo sich der
Spieler befindet. Wenn der Spieler die Falle im Vorraus oder von woanders
ausloesen kann, dann macht es nicht immer Sinn, die Falle tatsaechlich
auszuloesen oder dem Spieler den Effekt "zuzumuten".
Eine einfache Abfrage, ob sich der Spieler im gleichen Raum befindet, sieht so
aus:

    if (environment(spieler) && (environment(spieler) == this_object()))
    {
        // Spieler hat ein environment und das environment ist dieser Raum.
    }
    

Kapitel 1.3.2 Wer toetet den Spieler?

Ueblicherweise toetet man einen Spieler mit den in Kapitel 2 genannten
Moeglichkeiten. In diesen Moeglichkeiten ist das toetende Objekt der Raum.
Da aber nun der Raum keinen Namen hat, muss man diesem Raum einige Properties
setzen, die ueblicherweise erst einmal nur NPCs haben. Die Properties regeln,
"wer" in Kampfmeldungen, Angriffen und Todesmeldungen als Angreifer oder
Killer steht. Setzt man diese Werte direkt vor der Ausfuehrung einer
Todesfalle, dann kann man mehrere Todesfallen in einem Raum mit verschiedenen
Meldungen versehen.

    SetProp(P_NAME, "Ein gleissender Feuerball"); // Der Angreifer-Name
    SetProp(P_KILL_NAME, "Ein gleissender Feuerball");
        // Name fuer den Todeskanal.


Kapitel 1.4 Gimmicks

Der Vollstaendigkeit halber soll noch erwaehnt werden, dass man in der
Property P_ENEMY_DEATH_SEQUENCE das File einer (eigenen) Todessequenz ablegen
kann.


Kapitel 2 Technische Umsetzung von Todesfallen.

Den Tod durch oder den Angriff auf einen Spieler mittels einer Todesfalle kann
man auf verschiedene Weise bewerkstelligen. Die wichtigsten Moeglichkeiten
werden in den folgenden Unterkapiteln kurz erlaeutert. Es wird ausserdem auf
einige technische Feinheiten eingegangen, die zu beachten sind.


Kapitel 2.1 Tod durch die()

Die einfachste Moeglichkeit, einen Spieler sicher sterben zu lassen, ist der
Aufruf von die(). Die spaeter genannten Moeglichkeiten mit Ausnahme des
Faketods bewirken letztendlich auch einen Aufruf von die(), sollte der Spieler
sich nicht ausreichend dagegen geschuetzt haben. Bei dem direkten Aufruf kann
sich der Spieler nicht dagegen wehren. (Ausnahme: Geist sein, siehe oben.)
Die technische Umsetzung sieht wie folgt aus. Eventuelle Parameter fuer die()
sind der manpage fuer die() zu entnehmen.

    spieler->die(); // Jetzt ist der Spieler tot.


Kapitel 2.2 Todesfalle mit do_damage()

Die zweite Form der Todesfalle ist nicht ganz so aggressiv, wie die erste.
Hier wird dem Spieler eine feste oder zufaellige Zahl an Schadenspunkten 
zugefuegt. Der erlittene Schaden ist dabei nicht von Ruestung oder sonstigem
Schutz abhaengig, sondern der Schaden betraegt dem uebergebenen Wert. Ist der
uebergebene Wert hoeher als die Zahl der Lebenspunkte des Spielers, so stirbt
der Spieler. Nachlesen kann man dies in der manpage zu do_damage().
Diese Form der Todesfalle ist vermutlich die am haeufigsten verbreitete, da
der Magier am einfachsten die Konsequenzen ueberblicken kann, ohne den Spieler
zwingend zu toeten. Wichtig bei dieser Umsetzung ist, dass der Spieler keine
Angriffsmeldung oder dergleichen sieht. Er bekommt hoechstenfalls mit, dass er
ueberhaupt Schaden erhalten hat. Daher sollte vor dem Zufuegen des Schadens
eine Meldung ausgegeben werden, die dem Spieler anzeigt, weshalb er Schaden
bekam. (Bsp. "Ein Feuerball rast auf Dich zu und trifft Dich.")
Die technische Umsetzung sieht wie folgt aus:

    tell_object(spieler, "Ein Feuerball trifft Dich von hinten.\n");
    spieler->do_damage(10, this_object());
                         // Spieler erhaelt genau 10 Schadenspunkte und
                         // stirbt, wenn er dadurch unter 0 HP kommt.

Kapitel 2.2.1 Angepasste Todesfalle mit do_damage()

Moechte man, dass die Staerke der Todesfalle abhaengig vom Spieler ist, dann
kann man den in do_damage() uebergebenen Schaden abhaengig vom Spieler machen.
Dies ist in Gebieten sinnvoll, in denen Anfaenger und groessere Spieler
gleichermassen gefordert sein sollen (z.B. Quest). Folgendes Beispiel zeigt,
wie man eine Todesfalle macht, die dem Spieler etwa 1/4 seiner maximalen HP
abzieht.
    
    spieler->do_damage(spieler->QueryProp(P_MAX_HP) / 4);


Kapitel 2.3 Todesfalle mit reduce_hit_point

Mittels reduce_hit_point() ist es moeglich, eine nicht toedliche "Todes"-Falle
zu entwickeln. Dies kann genutzt werden, wenn man eine eigentlich toedliche
Falle machen will, die Auswirkungen eines Todes dem Spieler gegenueber aber zu
aggressiv waeren. Hierbei ist zu beachten, dass ein negativer Wert eine
Heilung bewirken wuerde. Daher ist zuvor eine Sicherheitsabfrage zu machen.
Wie bei do_damage() ist eine Meldung auszugeben, da reduce_hit_point eine
solche nicht generiert. Alles zusammen saehe der Part wie folgt aus:

    int schaden;
    tell_object(spieler, "Ein Feuerball trifft Dich.\n");
    // Hier den Schaden berechnen;
    // Z.B. schaden = 3;
    if (schaden < 1) schaden = 1; // Es soll mindestens 1 Schadenspunkt geben!
    spieler->reduce_hit_point(schaden);


Kapitel 2.4 Faketode

Faketode sind keine Tode im eigentlichen Sinn, da der Spieler von den
meisten negativen Auswirkungen verschont bleibt. Ein Faketod ist im Grunde
keine komplizierte Sache und eignet sich hervorragend fuer Anfaengergebiete.
Verschiedenen Auspraegungen (Bsp. Gibt es eine Leiche mit der Ausruestung des
Spielers oder behaelt er die Ausruestung?) gemein ist die Tatsache, dass der
Spieler augenscheinlich gestorben ist, jedoch kein die() aufgerufen wird. Es
werden todesbedingt also keine Attribute abgezogen, die Erfahrungspunkte
bleiben unangetastet usw, usf.
Die technische Umsetzung sieht wie folgt aus. Dabei ist zu beachten, dass der
Spieler Geist werden muss. Zuvor sollte wie bei den vorhergehenden Methoden
der Kill-Name etc. gesetzt werden.

    spieler->SetProp(P_GHOST,1);
    clone_object("/room/death/death_mark")->move(spieler);


Kapitel 2.4.1 Bedingte Faketode.

Man kann die Technik von Kapitel 2.3 mit den Faketoden kombinieren, indem man
testet, ob der Spieler auf oder unter 0 Lebenspunkte kommen wuerde, wenn man
anstatt reduce_hitpoint do_damage() machen wuerde und dann zusaetzlich zum
Abziehen der LP noch einen Faketod ausfuehrt. Dabei sind zwei Dinge zu
beachten:
Erstens sollte man keine zwei Meldungen ausgeben, also der Spieler sollte
keine zwei Feuerbaelle auf sich zufliegen sehen, obwohl nur einer da ist.
Zweitens sollte man nicht vergessen, dem Spieler dennoch die LP abzuziehen,
weil der Spieler ansonsten nach dem Entgeistern anstatt 1 LP noch soviele hat,
wie vor dem Feuerball-Angriff.
Die technische Umsetzung dieser Kombination wird an dieser Stelle nicht naeher
erlaeutert, da das Vorgehen aus den vorigen Kapiteln klar ist.


Kapitel 2.5 Angriff mittels Defend()

Eine realistische, wenn auch teilweise heikle Moeglichkeit fuer eine
Todesfalle ist der Aufruf der Defend()-Funktion des Spielers. Der Vorteil
von Defend ist, dass man unter Angabe der korrekten Parameter den Schutz der
Ruestungen eines Spielers oder auch die Gildenfaehigkeiten beruecksichtigt
werden. Daraus folgt, dass der als Beispiel verwendete Feuerball von einem vor
Feuer schuetzenden Objekt abgeschwaecht wuerde. Anwendungstechnisch gibt es
erst einmal keine Probleme, sofern einige wichtige Punkte beachtet werden, auf
die hier nun eingegangen wird. Einziger Nachteil besteht nur in der mangelnden
Erfahrung der einzutragenden Schadenswerte, da diese nicht 1:1 an die
Schadenspunkte gekoppelt sind, die an den Spieler weitergereicht werden.
Die technische Umsetzung fuer den Angriff eines Spielers durch eine Todesfalle
ueber Defend sieht wie folgt aus:

    #include 

    int schaden;
    schaden = 500+random(500); // Diese Werte machen u.U. viel Schaden.

    spieler->Defend(
        100,
        DT_FIRE,
        ([
            SP_SHOW_DAMAGE:0,
            SP_PHYSICAL_ATTACK:1
        ]),
        this_object()
    );

Wichtig sind hierbei folgende Punkte:
    1. Man sollte SP_SHOW_DAMAGE:0 im Mapping angeben, wenn man die Meldung
       z.B. "Ein Feuerball trifft Dich von hinten.\n" selbst ausgeben will.
       Tut man dies nicht, muss im Raum zusaetzlich P_GENDER, P_NAME, etc.
       auf einen sinnvollen Wert gesetzt werden, beispielsweise P_GENDER auf
       MALE und P_NAME auf "Feuerball".
    2. SP_PHYSICAL_ATTACK:1 sollte gesetzt werden, wenn des Spielers Ruestung
       fuer die Verteidigung beruecksichtigt werden soll. Genaueres findet man
       auf der Manpage von Defend().
    3. this_object() sollte immer angegeben werden, damit der Raum der
       Angreifer ist, anstatt ein nicht naeher bestimmtes Objekt. Dies koennte
       dann beispielsweise der Spieler selbst sein, was dazu fuehrt, dass im
       Falle eines Todes der Spieler als Killer von sich selbst genannt wird,
       anstatt "Ein Feuerball".




----------------------------------------------------------------------------
Last modified: Wed Feb 13 17:00:54 2002 by Bambi


zurück zur Übersicht

YOUTUBE | FACEBOOK | TWITTER | FEEDBACK | IMPRESSUM | DATENSCHUTZ 1992–2022 © MorgenGrauen.