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