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