blob: f3c0efea04c119a761baf6df84a3c70a94e4365f [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001//TODO:
2
3#pragma strong_types,rtt_checks
4
5inherit "/std/thing";
6
7#include <properties.h>
8#include <defines.h>
9#include <items/kraeuter/kraeuter.h>
10#include <items/kraeuter/trankattribute.h>
11#include <hook.h>
12#include <class.h>
13#include <new_skills.h>
14#include <wizlevels.h>
15
16#ifndef BS
17# define BS(x) break_string(x, 78)
18#endif
19
20#define allowed(x) (object_name(x)==PLANTMASTER)
21#define DRINK_POTION "lib_kraeutertrank_trinken"
22// for debug
23#define private public
24
25// Ablaufzeit des Tranks, ab dann keine Wirkung mehr
26private nosave int expiry;
27// Ablaufzeit der wirkungen des Trankes (0, wenn Trank nicht getrunken)
28private nosave int duration;
29// Trankattribute, vom Krautmaster schon skaliert, gekappt, beschraenkt auf
30// die jeweils max. positiven Effekte
31private nosave mapping data;
32// Klassen, die bei Schutz oder verstaerktem Schaden betroffen sind. Werte
33// ist der Schutz- oder Attackebonus/-malus.
34private nosave mapping att_classes;
35private nosave mapping prot_classes;
36// max. Summe von Giftstufen (P_POISON und P_LEVEL/10 bei CL_POISON).
37private nosave int prot_poison;
38// max. geheilte Summe von Krankheitslevel (P_LEVEL/5 in CL_DISEASE).
39private nosave int prot_disease;
40// nach Wirkung absteigend sortierte Liste der Effekte, wird beim ersten
41// Aufruf von DetermineStrongesEffect() befuellt.
42private nosave string* sorted_effects;
43
44mixed _query_data() {return data;}
45int _set_data(mixed d) {data=d; expiry = __INT_MAX__; return data!=0;}
46private string effect2colour();
47
48protected void create()
49{
50 if (object_name(this_object()) == __FILE__[0..<3])
51 {
52 set_next_reset(-1);
53 return;
54 }
55 ::create();
56 SetProp(P_GENDER, FEMALE);
57 SetProp(P_NAME, "Glasflasche");
58 SetProp(P_NAME_ADJ, ({"klein"}));
59 SetProp(P_VALUE, 10);
60 SetProp(P_WEIGHT, 100);
61 SetProp(P_KILL_NAME, "Ein Kraeutertrank");
62 SetProp(P_KILL_MSG, "%s hat sich wohl beim Anruehren vertan.");
63 AddId(({"glasflasche", "flasche"}));
64 AddAdjective(({"klein", "kleine"}));
65 AddCmd("trink|trinke&@ID", "cmd_trinken",
66 "Was willst Du trinken?");
67}
68
69protected void create_super()
70{
71 set_next_reset(-1);
72}
73
74static string _query_short()
75{
76 if (!clonep(ME))
77 return "Eine kleine Glasflasche";
78 return Name(WEN, 0)+(data==0 ? "" : " (gefuellt)");
79}
80
81static string _query_long()
82{
83 if (data==0)
84 return break_string(
85 "Eine kleine leere Glasflasche, die mit einem Korken verschlossen "
86 "ist.\n"
87 "Flaschen wie diese werden ueblicherweise verwendet, um darin "
88 "magische Traenke abzufuellen.", 78, 0, BS_LEAVE_MY_LFS);
89
90 return break_string(
91 "Eine kleine Glasflasche, die mit einer "+effect2colour()+"en "
92 "Fluessigkeit gefuellt ist.\n"
93 "Sie ist mit einem Korken verschlossen, um den Inhalt der "
94 "Flasche zu schuetzen.",78, 0, BS_LEAVE_MY_LFS);
95}
96
97private string num2desc(int bumms)
98{
99 switch(abs(bumms))
100 {
101 case 0..499:
102 return "ein wenig";
103 case 500..999:
104 return "so einiges";
105 case 1000..1499:
106 return "erheblich";
107 case 1500..2000:
108 return "unglaublich";
109 }
110 return "ungenehmigt viel"; // kommt hoffentlich nicht vor.
111}
112
113varargs private string DetermineStrongestEffect(int pos)
114{
115 // globale Werteliste befuellen, wenn da noch nichts drinsteht.
116 if ( !pointerp(sorted_effects) ) {
117 sorted_effects = sort_array(m_indices(data) & T_KRAUT_EFFECTS,
118 function int (string a, string b) {
119 return (abs(data[a])<=abs(data[b]));
120 });
121 }
122
123 // Zur Indizierung des Arrays muss <pos> bei Null starten, es wird
124 // aber mit der Bedeutung einer Ordinalzahl (erste, zweite, dritte, ...)
125 // uebergeben. Daher um 1 reduzieren.
126 --pos;
127
128 string ret;
129
130 // Im Array muss mindestens ein Eintrag stehen, sonst gibt's gar keinen
131 // Effekt.
132 if ( sizeof(sorted_effects) )
133 {
134 // Wenn der angefragte Index ausserhalb der Arraygrenzen liegt, wird
135 // angenommen, dass der erste bzw. letzte Eintrag gesucht waren.
136 if ( pos < 0 )
137 ret = sorted_effects[0];
138 else if ( pos >= sizeof(sorted_effects) )
139 ret = sorted_effects[<1];
140 else
141 ret = sorted_effects[pos];
142 }
143 return ret;
144}
145
146// Liefert zu dem maximal wirksamen Effekt die Farbe des Trankes zurueck.
147private string effect2colour()
148{
149 // Ist die Haltbarkeit schon abgelaufen, wird der Trank farblos.
150 if ( time() > expiry )
151 return "farblos";
152
153 // Namen des staerksten Effekts holen.
154 string effect = DetermineStrongestEffect(1);
155 mapping colours = ([
156 T_CARRY: "trueb braun",
157 T_DAMAGE_ANIMALS: "blutrot",
158 T_DAMAGE_MAGIC: "oktarinfarben",
159 T_DAMAGE_UNDEAD: "anthrazitfarben",
160 T_FLEE_TPORT: "schwefelgelb",
161 T_FROG: "schlammgruen",
162 T_HEAL_DISEASE: "perlmuttfarben",
163 T_HEAL_POISON: "gruen",
164 T_HEAL_SP: "blau",
165 T_HEAL_LP: "scharlachrot",
166 T_PROTECTION_ANIMALS: "metallisch grau",
167 T_PROTECTION_MAGIC: "violett",
168 T_PROTECTION_UNDEAD: "strahlend weiss",
169 T_SA_SPEED: "orangefarben",
170 T_SA_SPELL_PENETRATION: "stahlblau",
171 T_SA_DURATION: "pinkfarben",
172 ]);
173 string ret = colours[effect];
174 return stringp(ret) ? ret : "farblos";
175}
176
177// Wird gerufen, wenn die Wirkung des Trankes ablaufen soll.
178private void terminate_effects()
179{
180 tell_object(environment(),
181 "Die letzten Wirkungen des Kraeutertrankes klingen ab.\n");
182 remove(1);
183}
184
185// Von den Hooks H_HOOK_ATTACK_MOD und H_HOOK_DEFEND gerufen, erhoeht oder
186// verringert den Schaden gegen Lebenwesen bestimmter Klassen (Keys in
187// <classes>). Der Malus/Bonus steht als Wert zum jeweiligen Key in dem
188// Mapping.
189mixed hcallback(object hookSource, int hookid, mixed hookData)
190{
191 if (hookSource != environment())
192 return ({H_NO_MOD,hookData});
193 switch(hookid)
194 {
195 case H_HOOK_ATTACK_MOD:
196 foreach(string class, int modval : att_classes)
197 {
198 if (hookData[SI_ENEMY]->is_class_member(class))
199 {
200 // Yeah. Treffer. Schaden erhoehen oder verringern... ;)
201 hookData[SI_SKILLDAMAGE] += modval;
202 // Ende. keine weiteren Klassen pruefen.
203 return ({H_ALTERED, hookData});
204 }
205 }
206 break;
207 case H_HOOK_DEFEND:
208 // hookData: ({dam,dam_type,spell,enemy})
209 foreach(string class, int modval : prot_classes)
210 {
211 if (hookData[3]->is_class_member(class))
212 {
213 // Yeah. Treffer. Schaden erhoehen oder verringern... ;)
214 hookData[0] += modval;
215 // Ende. keine weiteren Klassen pruefen.
216 return ({H_ALTERED, hookData});
217 }
218 }
219 break;
220 case H_HOOK_INSERT:
221 // Wenn die Giftschutzkapazitaet noch ausreicht, wird das reinkommende
222 // Objekt zerstoert (und die Kapazitaet reduziert).
223 // hookData: neues object
224 if (prot_poison > 0
225 && hookData->is_class_member(CL_POISON))
226 {
227 // kostet ein Zehntel des Levels, aber min. 1.
228 int p=hookData->QueryProp(P_LEVEL) / 10 + 1;
229 if (p < prot_poison)
230 {
231 hookData->remove(1);
232 prot_poison-=p;
233 return ({H_CANCELLED, hookData});
234 }
235 }
236 // Wenn die Krankheitsschutzkapazitaet noch ausreicht, wird das reinkommende
237 // Objekt zerstoert (und die Kapazitaet reduziert).
238 // hookData: neues object
239 if (prot_disease > 0
240 && hookData->is_class_member(CL_DISEASE))
241 {
242 // kostet ein Fuenftel des Levels, aber min. 1.
243 int lvl = hookData->QueryProp(P_LEVEL) / 5 + 1;
244 if (lvl < prot_disease)
245 {
246 hookData->remove(1);
247 prot_disease-=lvl;
248 return ({H_CANCELLED, hookData});
249 }
250 }
251 break;
252 case H_HOOK_POISON:
253 // hookData: poisonval
254 // Alle Giftlevel werden reduziert auf 0 und von prot_poison
255 // abgezogen. Wenn das 0 ist, endet der Giftschutz.
256 if (prot_poison>0)
257 {
258 if (hookData < prot_poison)
259 {
260 prot_poison-=hookData;
261 hookData = 0;
262 }
263 else
264 {
265 hookData -= prot_poison;
266 prot_poison=0;
267 }
268 return ({H_ALTERED, hookData});
269 }
270 break;
271 }
272 return ({H_NO_MOD, hookData});
273}
274
275private int reg_hook(int hook, int hooktype)
276{
277 // Wenn schon registriert, zaehlt das auch als Erfolg.
278 if (environment()->HIsHookConsumer(hook, #'hcallback))
279 return 1;
280 int res = environment()->HRegisterToHook(hook, #'hcallback,
281 H_HOOK_OTHERPRIO(0), hooktype, duration);
282 if (res <= 0)
283 {
284 // wenn andere Fehler als -7 (zuviele Hooks registriert) vorkommen:
285 // Fehlermeldung ausgeben
286 if (res != -7)
287 tell_object(environment(),break_string(sprintf(
288 "Technischer Hinweis, den Du an einen Magier weitergeben "
289 "solltest: Beim Registrieren des Hooks %d gab es Fehler: %d\n",
290 hook, res),78));
291 return 0;
292 }
293 return 1;
294}
295
296// effekt: Wirkungswert des Tranks (muss negativ sein)
297// type: 1 fuer Gift, 0 fuer Krankheit
298private void ticktack(int effekt, int type)
299{
300 // Schaden tickt alle 5 Sekunden
301 int delay = 5;
302 // Der halbe Betrag des negativen Effekts wird als Schaden am
303 // Spieler verursacht.
304 // Berechnung: (Schaden pro Sekunde) * (Delay in Sekunden)
305 // in float rechnen ist hier sinnvoll, inkl. aufrunden (durch die +0.5)
306// int dam = to_int(((-0.5*effekt)/duration)*delay + 0.5);
307 int dam = to_int(0.5*abs(effekt)/data[T_EFFECT_DURATION]*delay + 0.5);
308
309 if (type)
310 tell_object(environment(),
311 break_string("Gift pulsiert brennend durch Deine Adern.",78));
312 else
313 tell_object(environment(),
314 break_string("Du fuehlst Dich schwach und elend, eine Erkrankung "
315 "zehrt an Deinen Kraeften.",78));
316
317 environment()->do_damage(dam, this_object());
318 call_out(#'ticktack, delay, effekt, type);
319}
320
321private int act_attr_heal_poison(int effekt)
322{
323 int erfolgreich;
324 tell_object(environment(), break_string(
325 "Du fuehlst, wie der Trank wie Feuer durch Deinen Koerper schiesst "
326 "und kruemmst Dich vor Schmerzen. Doch Momente spaeter laesst die "
327 "Tortur nach.",78));
328
329 // max. 40 Giftlevel heilen...
330 prot_poison = effekt / 50;
331
332 if (prot_poison < 0)
333 {
334 tell_object(environment(), BS(
335 "Bah! Der Trank schmeckt widerlich bitter. Wenn der mal nicht "
336 "giftig war."));
337 call_out(#'ticktack, 5, effekt, 1); // 1 => Gift
338 return 1;
339 }
340
341 // ab jetzt nur noch positive Wirkungen.
342
343 // P_POISON zuerst.
344 int poison = environment()->QueryProp(P_POISON);
345 if (poison)
346 {
347 if (poison <= prot_poison)
348 {
349 prot_poison -= poison;
350 environment()->SetProp(P_POISON,0);
351 if (!environment()->QueryProp(P_POISON))
352 ++erfolgreich;
353 }
354 else
355 {
356 poison -= prot_poison;
357 prot_poison=0;
358 environment()->SetProp(P_POISON, poison);
359 // Wenn erfolgreich, direkt Meldung und raus.
360 if (environment()->QueryProp(P_POISON) == poison)
361 {
362 tell_object(environment(), break_string(
363 "Ueberrascht stellst Du fest, dass Du Dich "
364 "besser fuehlst - der Trank hat Deine Vergiftung offenbar "
365 "gelindert.",78));
366 return 1;
367 }
368 }
369 }
370
371 // wenn Trank immer noch positiv (also noch WIrkung uebrig)
372 if (prot_poison > 0)
373 {
374 // Als naechstes Objekte suchen.
375 object *ob = filter_objects(all_inventory(environment()),
376 "is_class_member", CL_POISON);
377 foreach(object o: ob)
378 {
379 // Entgiften kostet ein Zehntel des Levels, aber min. 1.
380 poison = o->QueryProp(P_LEVEL);
381 if (poison <= prot_poison*10)
382 {
383 prot_poison -= poison/10 + 1;
384 o->SetProp(P_LEVEL, 0);
385 if (o->remove())
386 ++erfolgreich;
387 }
388 else
389 {
390 poison -= prot_poison * 10;
391 prot_poison = 0;
392 o->SetProp(P_LEVEL, poison);
393 if (o->QueryProp(P_LEVEL) == poison)
394 ++erfolgreich;
395 }
396 if (prot_poison <= 0)
397 break;
398 }
399 }
400
401 if (erfolgreich)
402 {
403 tell_object(environment(), break_string(
404 "Ueberrascht stellst Du fest, dass Du Dich viel besser fuehlst - der "
405 "Trank wirkt offenbar gegen Vergiftungen.",78));
406 }
407 else
408 {
409 tell_object(environment(), break_string(
410 "Eine Ahnung sagt Dir, dass der Trank irgendeine positive "
411 "Wirkung hat."));
412 }
413
414 // ggf. an die Hooks registrieren, wenn noch Schutzwirkung uebrig ist.
415 if (prot_poison > 0)
416 {
417 // Rueckgabewerte von HRegisterToHook speichern...
418 int *res = ({ reg_hook(H_HOOK_POISON, H_DATA_MODIFICATOR) });
419 res += ({ reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR) });
420 // Wenn alle versuchten Registrierungen erfolgreich waren...? Ansonsten
421 // andere Meldung... Ich bin noch nicht gluecklich, das hier so explizit
422 // auszugeben, aber ich weiss gerade sonst nicht, wie man drauf kommen
423 // soll, dass es ein Problem gibt.
424 if (sizeof(res) == sizeof(res & ({1})))
425 tell_object(environment(),
426 "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n");
427 else
428 {
429 // zumindest ein erfolg?
430 if (member(res, 1) > -1)
431 tell_object(environment(),
432 "Vielleicht haelt ein Teil dieser Wirkung ja sogar noch etwas an?\n");
433 }
434 }
435 return 1;
436}
437
438private int act_attr_heal_disease(int effekt)
439{
440 int erfolgreich;
441
442 // max. 40 Krankheitslevel heilen...
443 prot_disease = effekt / 50;
444
445 if (prot_disease > 0)
446 {
447 tell_object(environment(), break_string(
448 "Du fuehlst, wie der Trank in Deinem Bauch eine wohlige Waerme "
449 "verbreitet und laechelst unwillkuerlich.",78));
450
451 // Objekte suchen.
452 object *ob = filter_objects(all_inventory(environment()),
453 "is_class_member", CL_DISEASE);
454 foreach(object o: ob)
455 {
456 // Heilen kostet ein Fuenftel des Levels, aber min. 1.
457 int disease = o->QueryProp(P_LEVEL);
458 if (disease <= prot_disease*5)
459 {
460 prot_disease -= disease/5 + 1;
461 o->SetProp(P_LEVEL, 0);
462 if (o->remove())
463 ++erfolgreich;
464 }
465 else
466 {
467 disease -= prot_disease * 5;
468 prot_disease = 0;
469 o->SetProp(P_LEVEL, disease);
470 if (o->QueryProp(P_LEVEL) == disease)
471 ++erfolgreich;
472 }
473 if (prot_disease <= 0)
474 break;
475 }
476 }
477 else
478 {
479 tell_object(environment(), BS(
480 "Der Trank schmeckt eklig faulig. Dein Magen rebelliert umgehend. "
481 "Du kannst Deinen Brechreiz gerade noch unterdruecken, fuehlst "
482 "Dich aber krank."));
483 call_out(#'ticktack, 5, effekt, 0); // 0 => Krankheit
484 return 1;
485 }
486
487 if (erfolgreich)
488 {
489 tell_object(environment(), break_string(
490 "Entspannt durchatmend stellst Du fest, dass Du Dich viel besser fuehlst - der "
491 "Trank wirkt offenbar gegen Krankheiten.",78));
492 }
493 else
494 {
495 tell_object(environment(), break_string(
496 "Eine Ahnung sagt Dir, dass der Trank irgendeine positive "
497 "Wirkung hat."));
498 }
499
500 // ggf. an die Hooks registrieren.
501 if (prot_disease > 0)
502 {
503 // Registrierung erfolgreich...? Ansonsten andere Meldung... Ich bin
504 // noch nicht gluecklich, das hier so explizit auszugeben, aber ich
505 // weiss gerade sonst nicht, wie man drauf kommen soll, dass es ein
506 // Problem gibt.
507 if (reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR)==1)
508 tell_object(environment(),
509 "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n");
510
511 }
512 return 1;
513}
514
515private string num2desc_fight(int bumms)
516{
517 switch(abs(bumms))
518 {
519 case 0..499:
520 return "ein wenig";
521 case 500..999:
522 return "spuerbar";
523 case 1000..1499:
524 return "deutlich";
525 case 1500..2000:
526 return "erheblich";
527 }
528 return "ungenehmigt viel"; // kommt hoffentlich nicht vor.
529}
530
531// AN: Tiere sind: CL_ANIMAL, CL_FISH, CL_FROG, CL_INSECT, CL_MAMMAL,
532// CL_MAMMAL_LAND, CL_MAMMAL_WATER, CL_REPTILE, CL_ARACHNID, CL_BIRD
533private int act_attr_dam_animals(int effekt)
534{
535 if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
536 {
537 if (!mappingp(att_classes)) att_classes=m_allocate(1);
538 att_classes[CL_ANIMAL] = effekt/20;
539 tell_object(environment(), break_string(
540 "Du spuerst in Dir ein seltsames Verlangen aufsteigen, auf die Jagd "
541 "zu gehen - als wuerde Artemis persoenlich Deine Angriffe "
542 + num2desc_fight(effekt)
543 + " verbessern.",78));
544 return 1;
545 }
546 return 0;
547}
548
549// AN: Magische Wesen sollen sein: CL_ELEMENTAL, CL_ILLUSION, CL_SHADOW
550// CL_DRAGON, CL_DEMON, CL_SHAPECHANGER, CL_HARPY
551private int act_attr_dam_magical(int effekt)
552{
553 if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
554 {
555 if (!mappingp(att_classes)) att_classes=m_allocate(4);
556 att_classes[CL_DRAGON] = att_classes[CL_ELEMENTAL]
557 = att_classes[CL_SHADOW]
558 = att_classes[CL_ILLUSION]
559 = effekt/20;
560 tell_object(environment(), break_string(
561 "Merkwuerdig. Du hast gerade das Gefuehl, als fiele Dir der "
562 "Kampf gegen von Hekate beschenkte Wesen "
563 + num2desc_fight(effekt)
564 + " leichter.",78));
565 return 1;
566 }
567 return 0;
568}
569
570// AN: Untote sollen sein: CL_SKELETON, CL_GHOUL, CL_GHOST, CL_VAMPIRE
571// CL_ZOMBIE, CL_UNDEAD
572// Bloed ist nur, dass CL_UNDEAD alle anderen enthaelt bis auf CL_GHOST.
573// Also lassen wir die Unterscheidung und schrittweise Freischaltung
574// erst einmal sein.
575private int act_attr_dam_undead(int effekt)
576{
577 if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
578 {
579 // Zombies, Skelette, Ghule...
580 if (!mappingp(att_classes)) att_classes=m_allocate(1);
581 att_classes[CL_UNDEAD] = effekt/20;
582 tell_object(environment(), break_string(
583 "Auf einmal hast Du den Eindruck, dass die Kreaturen des "
584 "Hades Deinen Angriffen "
585 + num2desc_fight(effekt)
586 + " weniger entgegen zu setzen haben.",78));
587 return 1;
588 }
589 return 0;
590}
591
592private int act_attr_prot_animals(int effekt)
593{
594 if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
595 {
596 if (!mappingp(prot_classes)) prot_classes=m_allocate(1);
597 prot_classes[CL_ANIMAL] = effekt/20;
598 tell_object(environment(), break_string(
599 "Du hast das Gefuehl, dass Artemis ihre schuetzende Hand "
600 + num2desc_fight(effekt)
601 + " ueber Dich haelt.",78));
602 return 1;
603 }
604 return 0;
605}
606
607private int act_attr_prot_magical(int effekt)
608{
609 if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
610 {
611 if (!mappingp(prot_classes)) prot_classes=m_allocate(4);
612 prot_classes[CL_DRAGON] = prot_classes[CL_ELEMENTAL]
613 = prot_classes[CL_SHADOW]
614 = prot_classes[CL_ILLUSION]
615 = effekt/20;
616 tell_object(environment(), break_string(
617 "Du hast das Gefuehl, dass von Hekate beschenkte Wesenheiten Dir "
618 +num2desc_fight(effekt)
619 + " weniger anhaben koennen.",78));
620 return 1;
621 }
622 return 0;
623}
624
625private int act_attr_prot_undead(int effekt)
626{
627 if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
628 {
629 // Zombies, Skelette, Ghule...
630 if (!mappingp(prot_classes)) prot_classes=m_allocate(1);
631 prot_classes[CL_UNDEAD] = effekt/20;
632 tell_object(environment(), break_string(
633 "Du bist ploetzlich zuversichtlich, Angriffen der Kreaturen "
634 "des Hades "
635 + num2desc_fight(effekt)
636 + " besser widerstehen zu koennen.",78));
637 return 1;
638 }
639 return 0;
640}
641
642
643private int act_attr_tragen(int effekt)
644{
645 if ( IS_LEARNER(environment()) )
646 tell_object(environment(), sprintf("effekt: %d\n",effekt));
647 SetProp(P_WEIGHT, QueryProp(P_WEIGHT) - effekt*4);
648 tell_object(environment(),
649 BS("Du fuehlst Dich ploetzlich "+num2desc(effekt)
650 + (effekt>0 ? " entlastet" : " belastet") + "."));
651 return 1;
652}
653
654// Berechnet eine max. Verzoegerung der Wirkung abhaengig von der Wirkung und
655// einer Wirkschwelle. Der rel. Abstand von der Wirkschwelle (relativ zum max.
656// moeglichen Abstand) wird hierbei genutzt. Ausserdem ist die max.
657// Verzoegerung natuerlich die Wirkungsdauer des Trankes.
658// <duration> muss im Trank schon gesetzt sein.
659private int calc_max_delay(int effekt, int wirkschwelle)
660{
661 int abstand = abs(effekt - wirkschwelle);
662 if (!duration) duration = time()+600;
663 if ( IS_LEARNER(environment()) )
664 printf("calc_max_delay: %d\n",((duration-time()) * abstand) /
665 (2000-abs(wirkschwelle)));
666 return ((duration-time()) * abstand) / (2000-abs(wirkschwelle));
667}
668
669//TODO: die Zeitverzoegerung ist nen netter Effekt, aber zeitvergzoegerte
670//Tports sind oefter keine gute Idee -> noch pruefen
671//TODO: Die Zeitverzoegerung bei NO_TPORT_OUT auch nochmal pruefen
672private int act_attr_flee_tport(int effekt)
673{
674 // effekt > 0 teleportiert sofort zu halbwegs sicheren Orten
675 // -750 <= effekt < 0 teleportiert auch sofort zu nicht so dollen Orten
676 // -2000 <= effekt < -750 teleportiert spaeter zu bloeden Orten
677 if (effekt < -750 && effekt >= -2000)
678 {
679 // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
680 // effekt rufen, der eine instantane Reaktion ausloest.
681 // effekt - 2000 ist ein Hack, damit nicht nochmal verzoegert wird.
682 call_out(#'act_attr_flee_tport,
683 random(calc_max_delay(effekt, -750))+1, effekt - 2000);
684 tell_object(environment(),
685 "Deine Umgebung fuehlt sich nach und nach unwirklicher an.\n");
686 return 1;
687 }
688
689 // Effekte instantan ausloesen.
690
691 // wenn hier kein P_NO_TPORT erlaubt ist, mal gucken, ob wir spaeter noch
692 // zeit haben.
693 if (environment(environment())->QueryProp(P_NO_TPORT)==NO_TPORT_OUT)
694 {
695 tell_object(environment(),
696 BS("Irgendetwas haelt Dich an diesem Ort fest."));
697 int delay = duration - time();
698 // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen.
699 if (delay>10)
700 {
701 // AN/TODO: Kann das nicht auch ziemlich lang sein?
702 call_out(#'act_attr_flee_tport, random(delay), effekt);
703 }
704 return 0;
705 }
706
707 // den Hack von oben rueckgaengig machen, wir brauchen den originalen
708 // Effektwert zurueck.
709 if (effekt < -2000)
710 effekt += 2000;
711
712 string* dests;
713 if ( effekt > 0 )
714 {
715 switch(effekt)
716 {
717 case 0..499:
718 dests = ({"/d/inseln/zesstra/vulkanweg/room/r1",
719 "/d/fernwest/li/xian/lab2/grab2",
720 "/d/ebene/miril/schloss/heide11",
721 "/d/polar/alle/room/weg4_15",
722 "/d/dschungel/arathorn/tempel/room/t4-5",
723 "/d/anfaenger/arathorn/minitut/room/huette_"+
724 environment()->query_real_name(),
725 });
726 break;
727 case 500..749:
728 dests = ({"/d/ebene/bertram/ebene/wasser8",
729 "/d/ebene/room/gebuesch2_3",
730 "/d/polar/alle/room/eiswueste/eiswueste[4,6]",
731 });
732 if (environment()->QueryProp(P_REAL_RACE)!="Dunkelelf")
733 dests += ({"/d/unterwelt/padreic/kneipe/kneipe"});
734 break;
735 case 750..999:
736 dests = ({"/d/gebirge/silvana/cronoertal/room/tf4",
737 "/d/inseln/schiffe/floss",
738 "/d/polar/humni/hexen/room/leuchtkammer",
739 "/d/polar/gabylon/temong/rooms/anlegestelle",
740 });
741 break;
742 case 1000..1249:
743 dests = ({"/d/fernwest/shinobi/konfu_quest/room/insel4",
744 "/d/fernwest/ulo/mura/tokoro/haus4",
745 "/d/ebene/room/Halle/shalle14",
746 });
747 break;
748 case 1250..1499:
749 dests = ({"/d/gebirge/silvana/cronoertal/room/tf4",
750 "/gilden/zauberer",
751 "/d/gebirge/georg/room/w11"
752 });
753 break;
754 case 1500..1749:
755 dests = ({"/gilden/bierschuettler",
756 "/gilden/kaempfer",
757 "/d/wald/gundur/hobbitdorf/schrein",
758 "/d/vland/morgoth/room/city/rathalle",
759 });
760 break;
761 default:
762 dests = ({environment()->QueryProp(P_START_HOME)||
763 environment()->QueryDefaultHome(),
764 environment()->QueryDefaultHome(),
765 "/d/ebene/room/PortVain/po_haf2",
766 "/d/gebirge/room/he3x3",
767 "/d/ebene/room/huette",
768 });
769 break;
770 }
771 }
772 else if ( effekt < 0 )
773 {
774 switch(effekt)
775 {
776 case -499..0:
777 dests = ({"/d/polar/bendeigid/rooms/neskaya/neskay12",
778 "/players/ketos/gl/room/gl1x1",
779 "/d/inseln/zesstra/vulkanweg/room/r10",
780 "/d/vland/morgoth/room/kata/ktanke",
781 "/d/ebene/zardoz/burg/feuerrau",
782 });
783 break;
784 case -999..-500:
785 dests = ({"/d/ebene/room/Hoehle/hg6",
786 "/d/gebirge/silvana/warok/room/l3/wa_3x7",
787 "/d/vland/morgoth/room/kata/xkat03",
788 "/d/vland/morgoth/room/kata/kata5",
789 "/d/wald/yoru/ort/dravi/weg5",
790 "/d/wald/atamur/room/e83",
791 });
792 break;
793 case -1499..-1000:
794 dests = ({"/d/polar/bendeigid/rooms/pass/pass_e1",
795 "/d/ebene/room/gebuesch",
796 "/d/gebirge/silvana/warok/room/l1/wa_1x6",
797 "/d/vland/morgoth/room/kata/gkat17",
798 "/d/wald/atamur/room/e12",
799 });
800 if (environment()->QueryProp(P_REAL_RACE)=="Dunkelelf")
801 dests += ({"/d/unterwelt/padreic/kneipe/kneipe"});
802 break;
803 case -1749..-1500:
804 dests = ({"/d/dschungel/wurzel/t2",
805 "/d/vland/morgoth/room/kata/ukat26",
806 "/d/vland/morgoth/room/kata/h12",
807 "/d/gebirge/boing/sm/l5/m5x2",
808 });
809 if (environment()->QueryProp(P_GUILD)=="chaos")
810 dests+=({"/gilden/klerus"});
811 break;
812 default:
813 dests = ({"/d/ebene/rochus/room/sp10",
814 "/d/ebene/rochus/quest_3player/room/schacht10",
815 });
816 if ( IS_SEER(environment()) )
817 dests += ({"/d/wald/paracelsus/froom/sch_6e",
818 "/d/wald/paracelsus/froom/sch_9x",
819 "/d/wald/paracelsus/froom/sch2_6d",
820 });
821 break;
822 }
823 }
824 tell_object(environment(),
825 BS("Eine Kraft zerrt an Dir, die Welt verschwimmt..."));
826 if (environment()->move(dests[random(sizeof(dests))],M_TPORT) == MOVE_OK)
827 tell_object(environment(),
828 "Einen Moment spaeter bist Du ganz woanders.\n");
829 else
830 tell_object(environment(),
831 "Aber sonst passiert nichts.\n");
832 return 1;
833}
834
835private int act_attr_change_dimension(int effekt)
836{
837 // nur effekt >= 1000 aendern die Dimension instantan. ;-)
838 if (effekt > 0 && effekt < 1000)
839 {
840 // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
841 // effekt rufen, der eine instantane Reaktion ausloest.
842 call_out(#'act_attr_change_dimension,
843 random(calc_max_delay(effekt,1000))+1, 1000);
844 tell_object(environment(),BS("Um Dich herum wird alles "
845 "langsam grauer.\n"));
846 return 1;
847 }
848 // nur -600 < effekt < 0 aendern die Dimension instantan ;-)
849 if (effekt < -600)
850 {
851 // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
852 // effekt rufen, der eine instantane Reaktion ausloest.
853 call_out(#'act_attr_change_dimension,
854 random(calc_max_delay(effekt, -600))+1, -601);
855 tell_object(environment(),BS("Um Dich herum wird alles "
856 "langsam grauer.\n"));
857 return 1;
858 }
859 // Effekte instantan ausloesen.
860 // wenn hier kein Para-Trans erlaubt ist, mal gucken, ob wir spaeter noch
861 // zeit haben.
862 if (environment(environment())->QueryProp(P_NO_PARA_TRANS))
863 {
864 int delay = duration - time();
865 // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen.
866 if (delay>10)
867 {
868 call_out(#'act_attr_change_dimension, random(delay), effekt);
869 tell_object(environment(), BS("Die Welt um Dich wird ein "
870 "wenig grauer."));
871 }
872 return 0;
873 }
874 if ( effekt > 0 )
875 {
876 if ( environment()->QueryProp(P_PARA) > 0 )
877 {
878 environment()->SetProp(P_PARA, 0);
879 tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur "
880 "ein graues Wabern um Dich herum, bevor die Welt wieder "
881 "normal aussieht.\n"));
882 }
883 else
884 {
885 tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um "
886 "Dich herum grau aus."));
887 }
888 }
889 else if ( effekt < 0 )
890 {
891 if ( !environment()->QueryProp(P_PARA) )
892 {
893 environment()->SetProp(P_PARA, 1);
894 tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur "
895 "ein graues Wabern um Dich herum, bevor die Welt wieder "
896 "normal aussieht. Aber es bleibt ein ungutes Gefuehl.\n"));
897 }
898 else {
899 tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um "
900 "Dich herum grau aus."));
901 }
902 }
903 return 1;
904}
905
906private int act_attr_defrog(int effekt)
907{
908 // nur effekt > 1000 entfroscht instantan. ;-)
909 if (effekt > 0 && effekt < 1000)
910 {
911 // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
912 // effekt rufen, der eine instantane Reaktion ausloest.
913 call_out(#'act_attr_defrog, random(calc_max_delay(effekt,1000))+1, 1000);
914 tell_object(environment(),BS(
915 "Du hoerst ploetzlich lautes Gequake, was langsam immer leiser "
916 "wird.\n"));
917 return 1;
918 }
919 // nur -500 < effekt < 0 froscht instantan ;-)
920 if (effekt < -500)
921 {
922 // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
923 // effekt rufen, der eine instantane Reaktion ausloest.
924 call_out(#'act_attr_defrog, random(calc_max_delay(effekt, -500))+1, -501);
925 tell_object(environment(),BS(
926 "Du hoerst ploetzlich ganz leisess Gequake, was langsam immer lauter "
927 "wird.\n"));
928 return 1;
929 }
930
931 // Effekte instantan ausloesen.
932 if ( effekt > 0 )
933 {
934 if ( environment()->QueryProp(P_FROG) )
935 {
936 environment()->SetProp(P_FROG,0);
937 tell_object(PL, "Du fuehlst Dich deutlich weniger gruen.\n");
938 }
939 else
940 {
941 tell_object(environment(), break_string("Die Welt um Dich herum verliert "
942 "ploetzlich alle gruenen Farbtoene, die bald darauf allmaehlich "
943 "zurueckkehren.",78));
944 }
945 }
946 else if ( effekt < 0 ) {
947 if ( !environment()->QueryProp(P_FROG) ) {
948 environment()->SetProp(P_FROG, 1);
949 tell_object(environment(), "Quak!\n");
950 }
951 else {
952 tell_object(environment(), break_string("Deine Sicht verschwimmt, alles wird "
953 "intensiv gruen. Merkwuerdig. Zum Glueck ist das schnell wieder "
954 "vorbei.",78));
955 }
956 }
957 return 1;
958}
959
960private int act_attr_heal_lp(int effekt)
961{
962 if (effekt > 0)
963 {
964 tell_object(environment(),
965 BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
966 + " besser."));
967 environment()->restore_hit_points(effekt/10);
968 }
969 else
970 {
971 tell_object(environment(),
972 BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
973 + " schlechter."));
974 environment()->do_damage(-effekt/10);
975 }
976 return 1;
977}
978
979private int act_attr_heal_sp(int effekt)
980{
981 if (effekt > 0)
982 {
983 tell_object(environment(),
984 BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
985 + " konzentrierter."));
986 environment()->restore_spell_points(effekt/10);
987 }
988 else
989 {
990 tell_object(environment(),
991 BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
992 + " unkonzentrierter."));
993 environment()->reduce_spell_points(-effekt/10);
994 }
995 return 1;
996}
997
998
999private int modify_sa(string sa, int effekt)
1000{
1001 string msg;
1002 switch (sa)
1003 {
1004 case SA_SPEED:
1005 msg = "Du wirst gerade " + num2desc(effekt)
1006 +(effekt>0 ? " schneller." : " langsamer.");
1007 effekt = (effekt * 30) / 2000;
1008 break;
1009 case SA_DURATION:
1010 msg = "Du wirst gerade " + num2desc(effekt)
1011 +(effekt>0 ? " ausdauernder."
1012 : " weniger ausdauernd.");
1013 effekt = (effekt * 25) / 2000;
1014 break;
1015 case SA_SPELL_PENETRATION:
1016 msg = "Deine geistige Durchsetzungskraft hat sich gerade "
1017 + num2desc(effekt)
1018 +(effekt>0 ? " verbessert." : " verschlechtert.");
1019 effekt = (effekt * 30) / 2000;
1020 break;
1021 }
1022 if (environment()->ModifySkillAttribute(sa, effekt, duration-time()) ==
1023 SA_MOD_OK)
1024 {
1025 tell_object(environment(),BS(msg));
1026 return 1;
1027 }
1028 return 0;
1029}
1030
1031private int act_attr_change_sa_speed(int effekt)
1032{
1033 return modify_sa(SA_SPEED, effekt);
1034}
1035private int act_attr_change_sa_duration(int effekt)
1036{
1037 return modify_sa(SA_DURATION, effekt);
1038}
1039private int act_attr_change_sa_spell_penetration(int effekt)
1040{
1041 return modify_sa(SA_SPELL_PENETRATION, effekt);
1042}
1043
1044
1045// Spieler MUSS das environment() sein!
1046private void effekt()
1047{
1048 // Als erstes die Wirkungsdauer verwursten, einige Effekte brauchen
1049 // <duration>.
1050 // Wann laufen die Effekte denn spaetenstens ab?
1051 duration = time() + data[T_EFFECT_DURATION];
1052 call_out(#'terminate_effects, data[T_EFFECT_DURATION]);
1053
1054 // nur echte wirkungen beruecksichtigen, keine "Metadaten"
1055 mapping effects = data & T_KRAUT_EFFECTS;
1056 // fuer Spieler wird der Paratrans nicht stattfinden.
1057 if (!IS_SEER(environment()))
1058 m_delete(data, T_CHANGE_DIMENSION);
1059
1060 // Waehrend der Wirkung ist dieses Objekt schonmal unsichtbar.
1061 SetProp(P_INVIS, 1);
1062 // Gewicht nullen, bevor die Wirkungen aktiviert werden, da die
1063 // Wirkung von T_CARRY ansonsten wieder deaktiviert wuerde.
1064 SetProp(P_WEIGHT, 0);
1065 // neue, leere Flasche ins Inventar des Spielers bewegen.
1066 clone_object(TRANKITEM)->move(environment(),M_NOCHECK|M_SILENT);
1067
1068 // auftrennen in positive und negative Effekte. Keys mit Wert 0 werden
1069 // ignoriert.
1070 mapping peff = filter(effects, function int (string k, int val)
1071 {return val > 0;});
1072 mapping neff = filter(effects, function int (string k, int val)
1073 {return val < 0;});
1074 // erst die positiven, dann die negativen Wirkungen aktivieren
1075 // fuer jede Wirkung wird eine lfun act_<trankattribut>() gerufen, z.B.
1076 // act_attr_tragen() (-> act_T_CARRY() )
1077 mapping notactivated =
1078 filter(peff, function int (string k, int val)
1079 {return !funcall(symbol_function("act_"+k,this_object()), val);})
1080 +
1081 filter(neff, function int (string k, int val)
1082 {return !funcall(symbol_function("act_"+k,this_object()), val);});
1083 // Meldungen ausgeben ueber nicht aktivierte Wirkungen?
1084 // TODO
1085}
1086
1087static int cmd_trinken(string str, mixed *param)
1088{
1089 if (environment(this_object()) != PL)
1090 {
1091 write("Aus auf dem Boden liegende Flaschen trinkt es sich so "
1092 "schlecht!\n");
1093 }
1094 else if (data==0)
1095 {
1096 write("Die Flasche ist leer, Du muesstest erst etwas hineinfuellen.\n");
1097 }
1098 else if (time()>expiry)
1099 {
1100 write(BS("Irgendwie passiert nichts. Hattest Du vielleicht doch nur "
1101 "klares Wasser abgefuellt?"));
1102 data=0;
1103 }
1104 else {
1105 // Die Sperrzeit muss hier separat berechnet werden, weil eine
1106 // Anpassung der Wirkungsdauer im Krautmaster dazu fuehren wuerde,
1107 // dass es keine instantan wirkenden Traenke mehr gaebe.
1108 int blocktime = max(60+random(60), data[T_EFFECT_DURATION]);
1109 if(environment()->check_and_update_timed_key(blocktime, DRINK_POTION)==-1)
1110 {
1111 write(BS("Du oeffnest die Flasche und trinkst ihren Inhalt aus."));
1112 say(BS(PL->Name(WER, 0)+" oeffnet eine Flasche und trinkt sie in einem "
1113 "Schluck aus."));
1114 effekt();
1115 // TODO: reicht das hier aus, oder muss das noch an anderen Stellen
1116 // eingebaut werden?
1117 RemoveId(({"trank","kraeutertrank"}));
1118 }
1119 else {
1120 tell_object(environment(), BS(
1121 "Der letzte Trank wirkt noch ein wenig nach. Du kannst Deinem "
1122 "Magen nicht so bald schon den naechsten zumuten."));
1123 }
1124 }
1125 return 1;
1126}
1127
1128public nomask int Fill(object *plants)
1129{
1130 if (!pointerp(plants)) return -1;
1131 if (data) return -2; // schon voll
1132 data = PLANTMASTER->make_potion(plants);
1133 AddId(({"trank","kraeutertrank"}));
1134 if (mappingp(data))
1135 {
1136 // Pflanzen zerstoert der Master, wenns geklappt hat.
1137 expiry=time()+data[T_EXPIRE];
1138 SetProp(P_WEIGHT, 300);
1139 return 1;
1140 }
1141 return -3;
1142}
1143
1144void NotifyPlayerDeath(object vic, object killer, int exp)
1145{
1146 call_out("remove",1);
1147}