blob: 558503229ae73def6d72f899a6ffa1165fc79d84 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001Manpage zu Todesfallen. Technischer Leitfaden.
2
3
4Diese Manpage richtet sich an (Jung)magier, die Todesfallen nutzen wollen,
5um verschiedenste Dinge in ihren Gebieten damit zu erreichen. Dabei soll hier
6nicht diskutiert werden, wo welche Art von Todesfalle Sinn macht (oder auch
7nicht), sondern es sollen technische Aspekte bei der Programmierung einer
8solchen (Todes)falle aufgezeigt werden.
9
10
11Kapitel 1. Das Umfeld einer Todesfalle.
12
13Eine Todesfalle kann wie in spaeteren Kapiteln beschrieben, leicht umgesetzt
14werden. Damit sie sich auch so verhaelt wie sie soll, werden hier einige
15Sicherheitsaspekte behandelt, die durchgegangen werden sollten.
16
17
18Kapitel 1.1 Wer hat die Todesfalle ausgeloest?
19
20Die meisten Todesfallen werden durch ein Kommando ausgeloest, welches der
21Spieler ausfuehrt. Z.B.
22 druecke grossen roten knopf mit der aufschrift "todesfalle ausloesen"
23In diesem Fall existiert in der aufgerufenen Funktion die Moeglichkeit, ueber
24die Funktion this_player() den aktuell "gueltigen" Spieler auszufischen.
25Hierbei ist zu beachten, dass auch NPCs von dieser Funktion zurueckgegeben
26werden koennen. Auch im init() eines Raumes wird in this_player() der Spieler
27zurueckgegeben. Hierbei ist jedoch zu beachten, dass man immer schauen sollte,
28ob this_player() auch tatsaechlich ein Objekt zurueckliefert. Dies ist
29wichtig, da das init() (oder auch andere Funktionen) durch Gegenstaende, wie
30den Bumerang ausgeloest werden koennen. In diesem Fall steht in this_player()
31ueblicherweise kein Objekt. Die Sicherheitsabfrage sollte also wie folgt
32aussehen:
33
34 object spieler;
35 spieler = this_player();
36 if (spieler) // Ein Lebewesen?
37 {
38 ...
39 }
40 // else: Die Falle nicht ausloesen, da kein Lebewesen.
41
42
43Kapitel 1.2 Wer soll die Todesfalle nicht ausloesen?
44
45Oft kommt es vor, dass eine schoene Todesfalle von Gott und der Welt
46ausgeloest wird. Normalerweise moechte man aber nicht, dass "Gott" oder andere
47besondere Personengruppen diese Falle ausloesen. In den folgenden
48Unterkapiteln werden Abfragen gezeigt, um Personengruppen auszuschliessen.
49
50
51Kapitel 1.2.1 Todesfallen, die nicht auf Geister reagieren.
52
53Ueblicherweise sollen Todesfallen bei den Ausloesern Schaden verursachen. Bei
54Geistern laeuft dies jedoch ins Leere. Man koennte nun auf die Idee kommen,
55alternativ auf Geister zu reagieren.
56
57
58Kapitel 1.2.1.1 Nicht auf Geister reagieren.
59
60Angenommen, man hat einen Spieler (siehe oben), der im Begriff ist, die Falle
61auszuloesen. Man sollte nun pruefen, ob er ein Geist ist. Falls er kein Geist
62ist, kann man wie gewuenscht (Todesfalle) reagieren. Falls nicht, sollte man
63eine passende Meldung ausgeben. Z.B. "Als Geist hast Du nicht die notwendige
64Kraft, diesen Knopf zu druecken." Die technische Abfrage, ob ein Spieler ein
65Geist ist, sieht wie folgt aus.
66
67 if (!spieler->QueryProp(P_GHOST))
68 {
69 // ist _kein_ Geist, also Todesfalle.
70 }
71 else
72 {
73 // Alternativer Text, da Geist.
74 }
75
76
77Kapitel 1.2.1.2 Geister entgeistern.
78
79Da ausschliesslich die Rassenstartraeume entgeistern sollen, wurde dieser
80Punkt herausgenommen. Also: Es ist unerwuenscht, Geister zu entgeistern, um
81eine Todesfalle korrekt auszuloesen.
82
83
84Kapitel 1.2.1.3 Alternative fuer Geister.
85
86Rollenspieltechnisch gesehen macht es in den allerwenigsten Faellen Sinn,
87alternativ auf einen Geist zu reagieren. Der Vollstaendigkeit halber sei diese
88Loesung trotzdem erwaehnt. Ein Programmbeispiel sollte ohne grosse Muehe durch
89die schon vorhandenen Beispiele erstellt werden koennen.
90
91
92Kapitel 1.2.2 Gaeste und kleine Spieler.
93
94Rollenspieltechnisch macht es oft keinen Sinn, auf Gaeste oder kleine Spieler
95anders zu reagieren, als auf "den Rest". Moechte man jedoch einen besonderen
96Schutz fuer kleine Spieler einbauen ("Du hast zu grosse Angst, diesen grossen,
97roten Knopf zu druecken.") oder moechte man Gast-Sterbe-Testern den Hahn
98zudrehen (die Weicheier!), so sind folgende Abfragen unter Umstaenden
99sinnvoll.
100
101 // Gast?
102 if (spieler->QueryGuest())
103 {
104 // Ist ein Gast...
105 }
106 else
107 {
108 // Todesfalle. Kein Gast.
109 }
110
111 // Kleiner Spieler?
112 // In diesem Beispiel Abfrage des Spielerlevels.
113 if (spieler->QueryProp(P_LEVEL) < 10)
114 {
115 // Spielerlevel < 10 haben zuviel Angst.
116 }
117 else
118 {
119 // Spielerlevel >= 10 koennen hier ruhig die Todesfalle ausloesen.
120 }
121
122
123Kapitel 1.2.3 Magier
124
125Ueblicherweise sind Magier unsterblich und soll(t)en auch nicht
126(versehentlich) Todesfallen ausloesen. Dies ist vor allem dann der Fall, wenn
127"zufaellig" im gleichen Raum stehende Spieler Meldungen bekommen wuerden und
128dadurch gewarnt werden. Die technische Umsetzung, dass Magier Todesfallen
129nicht ausloesen, sieht wie folgt aus. Die Abfrage beruecksichtigt
130"mschau aus". Das heisst, Magier koennen sterben, wenn sie mschau aus gemacht
131haben.
132
133 #include <wizlevels.h>
134
135 if(IS_LEARNER(spieler) && spieler->QueryProp(P_WANTS_TO_LEARN))
136 {
137 // Magier: Man soll ihm mitteilen, dass er diese Aktion als Magier nicht
138 // tun darf.
139 }
140 else
141 {
142 // Kein Magier. => Todesfalle.
143 }
144
145
146Kapitel 1.3 Wichtige Punkte einer nicht sinnfreien Todesfalle.
147
148Eine Todesfalle ist, wie in Kapitel 2 gezeigt, eigentlich eine einfache Sache.
149Kombiniert mit den schon gezeitgten Sicherheitsabfragen hat man ueblicherweise
150ein gueltiges Opfer. Allgemeine Dinge, wie beispielsweise "Das Opfer sollte
151ein environment haben" werden hier uebergangen und nur die wesentlichen Dinge
152genannt, die beim potentiellen Toeten eines Spielers von Belang sind.
153
154
155Kapitel 1.3.1 Wo ist der Spieler?
156
157Ganz wichtig bei Todesfallen ist die Frage, wo sich der Spieler befindet.
158Wird eine Todesfalle in init() ausgeloest, dann befindet sich der Spieler
159schon in dem entsprechenden Raum, dessen init() nun ausgefuehrt wird. Das
160bedeutet, wenn der Spieler stirbt, sind seine Sachen zunaechst einmal in dem
161betreffenden Raum. In seltenen Faellen spielt es wirklich eine Rolle, wo sich
162der Spieler befindet und er hat eigentlich immer ein environment, in das seine
163Leiche dann kommt. Dennoch sollte man bei Todesfallen testen, wo sich der
164Spieler befindet. Wenn der Spieler die Falle im Vorraus oder von woanders
165ausloesen kann, dann macht es nicht immer Sinn, die Falle tatsaechlich
166auszuloesen oder dem Spieler den Effekt "zuzumuten".
167Eine einfache Abfrage, ob sich der Spieler im gleichen Raum befindet, sieht so
168aus:
169
170 if (environment(spieler) && (environment(spieler) == this_object()))
171 {
172 // Spieler hat ein environment und das environment ist dieser Raum.
173 }
174
175
176Kapitel 1.3.2 Wer toetet den Spieler?
177
178Ueblicherweise toetet man einen Spieler mit den in Kapitel 2 genannten
179Moeglichkeiten. In diesen Moeglichkeiten ist das toetende Objekt der Raum.
180Da aber nun der Raum keinen Namen hat, muss man diesem Raum einige Properties
181setzen, die ueblicherweise erst einmal nur NPCs haben. Die Properties regeln,
182"wer" in Kampfmeldungen, Angriffen und Todesmeldungen als Angreifer oder
183Killer steht. Setzt man diese Werte direkt vor der Ausfuehrung einer
184Todesfalle, dann kann man mehrere Todesfallen in einem Raum mit verschiedenen
185Meldungen versehen.
186
187 SetProp(P_NAME, "Ein gleissender Feuerball"); // Der Angreifer-Name
188 SetProp(P_KILL_NAME, "Ein gleissender Feuerball");
189 // Name fuer den Todeskanal.
190
191
192Kapitel 1.4 Gimmicks
193
194Der Vollstaendigkeit halber soll noch erwaehnt werden, dass man in der
195Property P_ENEMY_DEATH_SEQUENCE das File einer (eigenen) Todessequenz ablegen
196kann.
197
198
199Kapitel 2 Technische Umsetzung von Todesfallen.
200
201Den Tod durch oder den Angriff auf einen Spieler mittels einer Todesfalle kann
202man auf verschiedene Weise bewerkstelligen. Die wichtigsten Moeglichkeiten
203werden in den folgenden Unterkapiteln kurz erlaeutert. Es wird ausserdem auf
204einige technische Feinheiten eingegangen, die zu beachten sind.
205
206
207Kapitel 2.1 Tod durch die()
208
209Die einfachste Moeglichkeit, einen Spieler sicher sterben zu lassen, ist der
210Aufruf von die(). Die spaeter genannten Moeglichkeiten mit Ausnahme des
211Faketods bewirken letztendlich auch einen Aufruf von die(), sollte der Spieler
212sich nicht ausreichend dagegen geschuetzt haben. Bei dem direkten Aufruf kann
213sich der Spieler nicht dagegen wehren. (Ausnahme: Geist sein, siehe oben.)
214Die technische Umsetzung sieht wie folgt aus. Eventuelle Parameter fuer die()
215sind der manpage fuer die() zu entnehmen.
216
217 spieler->die(); // Jetzt ist der Spieler tot.
218
219
220Kapitel 2.2 Todesfalle mit do_damage()
221
222Die zweite Form der Todesfalle ist nicht ganz so aggressiv, wie die erste.
223Hier wird dem Spieler eine feste oder zufaellige Zahl an Schadenspunkten
224zugefuegt. Der erlittene Schaden ist dabei nicht von Ruestung oder sonstigem
225Schutz abhaengig, sondern der Schaden betraegt dem uebergebenen Wert. Ist der
226uebergebene Wert hoeher als die Zahl der Lebenspunkte des Spielers, so stirbt
227der Spieler. Nachlesen kann man dies in der manpage zu do_damage().
228Diese Form der Todesfalle ist vermutlich die am haeufigsten verbreitete, da
229der Magier am einfachsten die Konsequenzen ueberblicken kann, ohne den Spieler
230zwingend zu toeten. Wichtig bei dieser Umsetzung ist, dass der Spieler keine
231Angriffsmeldung oder dergleichen sieht. Er bekommt hoechstenfalls mit, dass er
232ueberhaupt Schaden erhalten hat. Daher sollte vor dem Zufuegen des Schadens
233eine Meldung ausgegeben werden, die dem Spieler anzeigt, weshalb er Schaden
234bekam. (Bsp. "Ein Feuerball rast auf Dich zu und trifft Dich.")
235Die technische Umsetzung sieht wie folgt aus:
236
237 tell_object(spieler, "Ein Feuerball trifft Dich von hinten.\n");
238 spieler->do_damage(10, this_object());
239 // Spieler erhaelt genau 10 Schadenspunkte und
240 // stirbt, wenn er dadurch unter 0 HP kommt.
241
242Kapitel 2.2.1 Angepasste Todesfalle mit do_damage()
243
244Moechte man, dass die Staerke der Todesfalle abhaengig vom Spieler ist, dann
245kann man den in do_damage() uebergebenen Schaden abhaengig vom Spieler machen.
246Dies ist in Gebieten sinnvoll, in denen Anfaenger und groessere Spieler
247gleichermassen gefordert sein sollen (z.B. Quest). Folgendes Beispiel zeigt,
248wie man eine Todesfalle macht, die dem Spieler etwa 1/4 seiner maximalen HP
249abzieht.
250
251 spieler->do_damage(spieler->QueryProp(P_MAX_HP) / 4);
252
253
254Kapitel 2.3 Todesfalle mit reduce_hit_point
255
256Mittels reduce_hit_point() ist es moeglich, eine nicht toedliche "Todes"-Falle
257zu entwickeln. Dies kann genutzt werden, wenn man eine eigentlich toedliche
258Falle machen will, die Auswirkungen eines Todes dem Spieler gegenueber aber zu
259aggressiv waeren. Hierbei ist zu beachten, dass ein negativer Wert eine
260Heilung bewirken wuerde. Daher ist zuvor eine Sicherheitsabfrage zu machen.
261Wie bei do_damage() ist eine Meldung auszugeben, da reduce_hit_point eine
262solche nicht generiert. Alles zusammen saehe der Part wie folgt aus:
263
264 int schaden;
265 tell_object(spieler, "Ein Feuerball trifft Dich.\n");
266 // Hier den Schaden berechnen;
267 // Z.B. schaden = 3;
268 if (schaden < 1) schaden = 1; // Es soll mindestens 1 Schadenspunkt geben!
269 spieler->reduce_hit_point(schaden);
270
271
272Kapitel 2.4 Faketode
273
274Faketode sind keine Tode im eigentlichen Sinn, da der Spieler von den
275meisten negativen Auswirkungen verschont bleibt. Ein Faketod ist im Grunde
276keine komplizierte Sache und eignet sich hervorragend fuer Anfaengergebiete.
277Verschiedenen Auspraegungen (Bsp. Gibt es eine Leiche mit der Ausruestung des
278Spielers oder behaelt er die Ausruestung?) gemein ist die Tatsache, dass der
279Spieler augenscheinlich gestorben ist, jedoch kein die() aufgerufen wird. Es
280werden todesbedingt also keine Attribute abgezogen, die Erfahrungspunkte
281bleiben unangetastet usw, usf.
282Die technische Umsetzung sieht wie folgt aus. Dabei ist zu beachten, dass der
283Spieler Geist werden muss. Zuvor sollte wie bei den vorhergehenden Methoden
284der Kill-Name etc. gesetzt werden.
285
286 spieler->SetProp(P_GHOST,1);
287 clone_object("/room/death/death_mark")->move(spieler);
288
289
290Kapitel 2.4.1 Bedingte Faketode.
291
292Man kann die Technik von Kapitel 2.3 mit den Faketoden kombinieren, indem man
293testet, ob der Spieler auf oder unter 0 Lebenspunkte kommen wuerde, wenn man
294anstatt reduce_hitpoint do_damage() machen wuerde und dann zusaetzlich zum
295Abziehen der LP noch einen Faketod ausfuehrt. Dabei sind zwei Dinge zu
296beachten:
297Erstens sollte man keine zwei Meldungen ausgeben, also der Spieler sollte
298keine zwei Feuerbaelle auf sich zufliegen sehen, obwohl nur einer da ist.
299Zweitens sollte man nicht vergessen, dem Spieler dennoch die LP abzuziehen,
300weil der Spieler ansonsten nach dem Entgeistern anstatt 1 LP noch soviele hat,
301wie vor dem Feuerball-Angriff.
302Die technische Umsetzung dieser Kombination wird an dieser Stelle nicht naeher
303erlaeutert, da das Vorgehen aus den vorigen Kapiteln klar ist.
304
305
306Kapitel 2.5 Angriff mittels Defend()
307
308Eine realistische, wenn auch teilweise heikle Moeglichkeit fuer eine
309Todesfalle ist der Aufruf der Defend()-Funktion des Spielers. Der Vorteil
310von Defend ist, dass man unter Angabe der korrekten Parameter den Schutz der
311Ruestungen eines Spielers oder auch die Gildenfaehigkeiten beruecksichtigt
312werden. Daraus folgt, dass der als Beispiel verwendete Feuerball von einem vor
313Feuer schuetzenden Objekt abgeschwaecht wuerde. Anwendungstechnisch gibt es
314erst einmal keine Probleme, sofern einige wichtige Punkte beachtet werden, auf
315die hier nun eingegangen wird. Einziger Nachteil besteht nur in der mangelnden
316Erfahrung der einzutragenden Schadenswerte, da diese nicht 1:1 an die
317Schadenspunkte gekoppelt sind, die an den Spieler weitergereicht werden.
318Die technische Umsetzung fuer den Angriff eines Spielers durch eine Todesfalle
319ueber Defend sieht wie folgt aus:
320
321 #include <new_skills.h>
322
323 int schaden;
324 schaden = 500+random(500); // Diese Werte machen u.U. viel Schaden.
325
326 spieler->Defend(
327 100,
328 DT_FIRE,
329 ([
330 SP_SHOW_DAMAGE:0,
331 SP_PHYSICAL_ATTACK:1
332 ]),
333 this_object()
334 );
335
336Wichtig sind hierbei folgende Punkte:
337 1. Man sollte SP_SHOW_DAMAGE:0 im Mapping angeben, wenn man die Meldung
338 z.B. "Ein Feuerball trifft Dich von hinten.\n" selbst ausgeben will.
339 Tut man dies nicht, muss im Raum zusaetzlich P_GENDER, P_NAME, etc.
340 auf einen sinnvollen Wert gesetzt werden, beispielsweise P_GENDER auf
341 MALE und P_NAME auf "Feuerball".
342 2. SP_PHYSICAL_ATTACK:1 sollte gesetzt werden, wenn des Spielers Ruestung
343 fuer die Verteidigung beruecksichtigt werden soll. Genaueres findet man
344 auf der Manpage von Defend().
345 3. this_object() sollte immer angegeben werden, damit der Raum der
346 Angreifer ist, anstatt ein nicht naeher bestimmtes Objekt. Dies koennte
347 dann beispielsweise der Spieler selbst sein, was dazu fuehrt, dass im
348 Falle eines Todes der Spieler als Killer von sich selbst genannt wird,
349 anstatt "Ein Feuerball".
350
351
352
353
354----------------------------------------------------------------------------
355Last modified: Wed Feb 13 17:00:54 2002 by Bambi