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