| //TODO: |
| |
| #pragma strong_types,rtt_checks |
| |
| inherit "/std/thing"; |
| |
| #include <properties.h> |
| #include <defines.h> |
| #include <items/kraeuter/kraeuter.h> |
| #include <items/kraeuter/trankattribute.h> |
| #include <hook.h> |
| #include <class.h> |
| #include <new_skills.h> |
| #include <wizlevels.h> |
| |
| #ifndef BS |
| # define BS(x) break_string(x, 78) |
| #endif |
| |
| #define allowed(x) (object_name(x)==PLANTMASTER) |
| #define DRINK_POTION "lib_kraeutertrank_trinken" |
| // for debug |
| #define private public |
| |
| // Ablaufzeit des Tranks, ab dann keine Wirkung mehr |
| private nosave int expiry; |
| // Ablaufzeit der wirkungen des Trankes (0, wenn Trank nicht getrunken) |
| private nosave int duration; |
| // Trankattribute, vom Krautmaster schon skaliert, gekappt, beschraenkt auf |
| // die jeweils max. positiven Effekte |
| private nosave mapping data; |
| // Klassen, die bei Schutz oder verstaerktem Schaden betroffen sind. Werte |
| // ist der Schutz- oder Attackebonus/-malus. |
| private nosave mapping att_classes; |
| private nosave mapping prot_classes; |
| // max. Summe von Giftstufen (P_POISON und P_LEVEL/10 bei CL_POISON). |
| private nosave int prot_poison; |
| // max. geheilte Summe von Krankheitslevel (P_LEVEL/5 in CL_DISEASE). |
| private nosave int prot_disease; |
| // nach Wirkung absteigend sortierte Liste der Effekte, wird beim ersten |
| // Aufruf von DetermineStrongesEffect() befuellt. |
| private nosave string* sorted_effects; |
| |
| mixed _query_data() {return data;} |
| int _set_data(mixed d) {data=d; expiry = __INT_MAX__; return data!=0;} |
| private string effect2colour(); |
| |
| protected void create() |
| { |
| if (object_name(this_object()) == __FILE__[0..<3]) |
| { |
| set_next_reset(-1); |
| return; |
| } |
| ::create(); |
| SetProp(P_GENDER, FEMALE); |
| SetProp(P_NAME, "Glasflasche"); |
| SetProp(P_NAME_ADJ, ({"klein"})); |
| SetProp(P_VALUE, 10); |
| SetProp(P_WEIGHT, 100); |
| SetProp(P_KILL_NAME, "Ein Kraeutertrank"); |
| SetProp(P_KILL_MSG, "%s hat sich wohl beim Anruehren vertan."); |
| AddId(({"glasflasche", "flasche"})); |
| AddAdjective(({"klein", "kleine"})); |
| AddCmd("trink|trinke&@ID", "cmd_trinken", |
| "Was willst Du trinken?"); |
| } |
| |
| protected void create_super() |
| { |
| set_next_reset(-1); |
| } |
| |
| static string _query_short() |
| { |
| if (!clonep(ME)) |
| return "Eine kleine Glasflasche"; |
| return Name(WEN, 0)+(data==0 ? "" : " (gefuellt)"); |
| } |
| |
| static string _query_long() |
| { |
| if (data==0) |
| return break_string( |
| "Eine kleine leere Glasflasche, die mit einem Korken verschlossen " |
| "ist.\n" |
| "Flaschen wie diese werden ueblicherweise verwendet, um darin " |
| "magische Traenke abzufuellen.", 78, 0, BS_LEAVE_MY_LFS); |
| |
| return break_string( |
| "Eine kleine Glasflasche, die mit einer "+effect2colour()+"en " |
| "Fluessigkeit gefuellt ist.\n" |
| "Sie ist mit einem Korken verschlossen, um den Inhalt der " |
| "Flasche zu schuetzen.",78, 0, BS_LEAVE_MY_LFS); |
| } |
| |
| private string num2desc(int bumms) |
| { |
| switch(abs(bumms)) |
| { |
| case 0..499: |
| return "ein wenig"; |
| case 500..999: |
| return "so einiges"; |
| case 1000..1499: |
| return "erheblich"; |
| case 1500..2000: |
| return "unglaublich"; |
| } |
| return "ungenehmigt viel"; // kommt hoffentlich nicht vor. |
| } |
| |
| varargs private string DetermineStrongestEffect(int pos) |
| { |
| // globale Werteliste befuellen, wenn da noch nichts drinsteht. |
| if ( !pointerp(sorted_effects) ) { |
| sorted_effects = sort_array(m_indices(data) & T_KRAUT_EFFECTS, |
| function int (string a, string b) { |
| return (abs(data[a])<=abs(data[b])); |
| }); |
| } |
| |
| // Zur Indizierung des Arrays muss <pos> bei Null starten, es wird |
| // aber mit der Bedeutung einer Ordinalzahl (erste, zweite, dritte, ...) |
| // uebergeben. Daher um 1 reduzieren. |
| --pos; |
| |
| string ret; |
| |
| // Im Array muss mindestens ein Eintrag stehen, sonst gibt's gar keinen |
| // Effekt. |
| if ( sizeof(sorted_effects) ) |
| { |
| // Wenn der angefragte Index ausserhalb der Arraygrenzen liegt, wird |
| // angenommen, dass der erste bzw. letzte Eintrag gesucht waren. |
| if ( pos < 0 ) |
| ret = sorted_effects[0]; |
| else if ( pos >= sizeof(sorted_effects) ) |
| ret = sorted_effects[<1]; |
| else |
| ret = sorted_effects[pos]; |
| } |
| return ret; |
| } |
| |
| // Liefert zu dem maximal wirksamen Effekt die Farbe des Trankes zurueck. |
| private string effect2colour() |
| { |
| // Ist die Haltbarkeit schon abgelaufen, wird der Trank farblos. |
| if ( time() > expiry ) |
| return "farblos"; |
| |
| // Namen des staerksten Effekts holen. |
| string effect = DetermineStrongestEffect(1); |
| mapping colours = ([ |
| T_CARRY: "trueb braun", |
| T_DAMAGE_ANIMALS: "blutrot", |
| T_DAMAGE_MAGIC: "oktarinfarben", |
| T_DAMAGE_UNDEAD: "anthrazitfarben", |
| T_FLEE_TPORT: "schwefelgelb", |
| T_FROG: "schlammgruen", |
| T_HEAL_DISEASE: "perlmuttfarben", |
| T_HEAL_POISON: "gruen", |
| T_HEAL_SP: "blau", |
| T_HEAL_LP: "scharlachrot", |
| T_PROTECTION_ANIMALS: "metallisch grau", |
| T_PROTECTION_MAGIC: "violett", |
| T_PROTECTION_UNDEAD: "strahlend weiss", |
| T_SA_SPEED: "orangefarben", |
| T_SA_SPELL_PENETRATION: "stahlblau", |
| T_SA_DURATION: "pinkfarben", |
| ]); |
| string ret = colours[effect]; |
| return stringp(ret) ? ret : "farblos"; |
| } |
| |
| // Wird gerufen, wenn die Wirkung des Trankes ablaufen soll. |
| private void terminate_effects() |
| { |
| tell_object(environment(), |
| "Die letzten Wirkungen des Kraeutertrankes klingen ab.\n"); |
| remove(1); |
| } |
| |
| // Von den Hooks H_HOOK_ATTACK_MOD und H_HOOK_DEFEND gerufen, erhoeht oder |
| // verringert den Schaden gegen Lebenwesen bestimmter Klassen (Keys in |
| // <classes>). Der Malus/Bonus steht als Wert zum jeweiligen Key in dem |
| // Mapping. |
| mixed hcallback(object hookSource, int hookid, mixed hookData) |
| { |
| if (hookSource != environment()) |
| return ({H_NO_MOD,hookData}); |
| switch(hookid) |
| { |
| case H_HOOK_ATTACK_MOD: |
| foreach(string class, int modval : att_classes) |
| { |
| if (hookData[SI_ENEMY]->is_class_member(class)) |
| { |
| // Yeah. Treffer. Schaden erhoehen oder verringern... ;) |
| hookData[SI_SKILLDAMAGE] += modval; |
| // Ende. keine weiteren Klassen pruefen. |
| return ({H_ALTERED, hookData}); |
| } |
| } |
| break; |
| case H_HOOK_DEFEND: |
| // hookData: ({dam,dam_type,spell,enemy}) |
| foreach(string class, int modval : prot_classes) |
| { |
| if (hookData[3]->is_class_member(class)) |
| { |
| // Yeah. Treffer. Schaden erhoehen oder verringern... ;) |
| hookData[0] += modval; |
| // Ende. keine weiteren Klassen pruefen. |
| return ({H_ALTERED, hookData}); |
| } |
| } |
| break; |
| case H_HOOK_INSERT: |
| // Wenn die Giftschutzkapazitaet noch ausreicht, wird das reinkommende |
| // Objekt zerstoert (und die Kapazitaet reduziert). |
| // hookData: neues object |
| if (prot_poison > 0 |
| && hookData->is_class_member(CL_POISON)) |
| { |
| // kostet ein Zehntel des Levels, aber min. 1. |
| int p=hookData->QueryProp(P_LEVEL) / 10 + 1; |
| if (p < prot_poison) |
| { |
| hookData->remove(1); |
| prot_poison-=p; |
| return ({H_CANCELLED, hookData}); |
| } |
| } |
| // Wenn die Krankheitsschutzkapazitaet noch ausreicht, wird das reinkommende |
| // Objekt zerstoert (und die Kapazitaet reduziert). |
| // hookData: neues object |
| if (prot_disease > 0 |
| && hookData->is_class_member(CL_DISEASE)) |
| { |
| // kostet ein Fuenftel des Levels, aber min. 1. |
| int lvl = hookData->QueryProp(P_LEVEL) / 5 + 1; |
| if (lvl < prot_disease) |
| { |
| hookData->remove(1); |
| prot_disease-=lvl; |
| return ({H_CANCELLED, hookData}); |
| } |
| } |
| break; |
| case H_HOOK_POISON: |
| // hookData: poisonval |
| // Alle Giftlevel werden reduziert auf 0 und von prot_poison |
| // abgezogen. Wenn das 0 ist, endet der Giftschutz. |
| if (prot_poison>0) |
| { |
| if (hookData < prot_poison) |
| { |
| prot_poison-=hookData; |
| hookData = 0; |
| } |
| else |
| { |
| hookData -= prot_poison; |
| prot_poison=0; |
| } |
| return ({H_ALTERED, hookData}); |
| } |
| break; |
| } |
| return ({H_NO_MOD, hookData}); |
| } |
| |
| private int reg_hook(int hook, int hooktype) |
| { |
| // Wenn schon registriert, zaehlt das auch als Erfolg. |
| if (environment()->HIsHookConsumer(hook, #'hcallback)) |
| return 1; |
| int res = environment()->HRegisterToHook(hook, #'hcallback, |
| H_HOOK_OTHERPRIO(0), hooktype, duration); |
| if (res <= 0) |
| { |
| // wenn andere Fehler als -7 (zuviele Hooks registriert) vorkommen: |
| // Fehlermeldung ausgeben |
| if (res != -7) |
| tell_object(environment(),break_string(sprintf( |
| "Technischer Hinweis, den Du an einen Magier weitergeben " |
| "solltest: Beim Registrieren des Hooks %d gab es Fehler: %d\n", |
| hook, res),78)); |
| return 0; |
| } |
| return 1; |
| } |
| |
| // effekt: Wirkungswert des Tranks (muss negativ sein) |
| // type: 1 fuer Gift, 0 fuer Krankheit |
| private void ticktack(int effekt, int type) |
| { |
| // Schaden tickt alle 5 Sekunden |
| int delay = 5; |
| // Der halbe Betrag des negativen Effekts wird als Schaden am |
| // Spieler verursacht. |
| // Berechnung: (Schaden pro Sekunde) * (Delay in Sekunden) |
| // in float rechnen ist hier sinnvoll, inkl. aufrunden (durch die +0.5) |
| // int dam = to_int(((-0.5*effekt)/duration)*delay + 0.5); |
| int dam = to_int(0.5*abs(effekt)/data[T_EFFECT_DURATION]*delay + 0.5); |
| |
| if (type) |
| tell_object(environment(), |
| break_string("Gift pulsiert brennend durch Deine Adern.",78)); |
| else |
| tell_object(environment(), |
| break_string("Du fuehlst Dich schwach und elend, eine Erkrankung " |
| "zehrt an Deinen Kraeften.",78)); |
| |
| environment()->do_damage(dam, this_object()); |
| call_out(#'ticktack, delay, effekt, type); |
| } |
| |
| private int act_attr_heal_poison(int effekt) |
| { |
| int erfolgreich; |
| tell_object(environment(), break_string( |
| "Du fuehlst, wie der Trank wie Feuer durch Deinen Koerper schiesst " |
| "und kruemmst Dich vor Schmerzen. Doch Momente spaeter laesst die " |
| "Tortur nach.",78)); |
| |
| // max. 40 Giftlevel heilen... |
| prot_poison = effekt / 50; |
| |
| if (prot_poison < 0) |
| { |
| tell_object(environment(), BS( |
| "Bah! Der Trank schmeckt widerlich bitter. Wenn der mal nicht " |
| "giftig war.")); |
| call_out(#'ticktack, 5, effekt, 1); // 1 => Gift |
| return 1; |
| } |
| |
| // ab jetzt nur noch positive Wirkungen. |
| |
| // P_POISON zuerst. |
| int poison = environment()->QueryProp(P_POISON); |
| if (poison) |
| { |
| if (poison <= prot_poison) |
| { |
| prot_poison -= poison; |
| environment()->SetProp(P_POISON,0); |
| if (!environment()->QueryProp(P_POISON)) |
| ++erfolgreich; |
| } |
| else |
| { |
| poison -= prot_poison; |
| prot_poison=0; |
| environment()->SetProp(P_POISON, poison); |
| // Wenn erfolgreich, direkt Meldung und raus. |
| if (environment()->QueryProp(P_POISON) == poison) |
| { |
| tell_object(environment(), break_string( |
| "Ueberrascht stellst Du fest, dass Du Dich " |
| "besser fuehlst - der Trank hat Deine Vergiftung offenbar " |
| "gelindert.",78)); |
| return 1; |
| } |
| } |
| } |
| |
| // wenn Trank immer noch positiv (also noch WIrkung uebrig) |
| if (prot_poison > 0) |
| { |
| // Als naechstes Objekte suchen. |
| object *ob = filter_objects(all_inventory(environment()), |
| "is_class_member", CL_POISON); |
| foreach(object o: ob) |
| { |
| // Entgiften kostet ein Zehntel des Levels, aber min. 1. |
| poison = o->QueryProp(P_LEVEL); |
| if (poison <= prot_poison*10) |
| { |
| prot_poison -= poison/10 + 1; |
| o->SetProp(P_LEVEL, 0); |
| if (o->remove()) |
| ++erfolgreich; |
| } |
| else |
| { |
| poison -= prot_poison * 10; |
| prot_poison = 0; |
| o->SetProp(P_LEVEL, poison); |
| if (o->QueryProp(P_LEVEL) == poison) |
| ++erfolgreich; |
| } |
| if (prot_poison <= 0) |
| break; |
| } |
| } |
| |
| if (erfolgreich) |
| { |
| tell_object(environment(), break_string( |
| "Ueberrascht stellst Du fest, dass Du Dich viel besser fuehlst - der " |
| "Trank wirkt offenbar gegen Vergiftungen.",78)); |
| } |
| else |
| { |
| tell_object(environment(), break_string( |
| "Eine Ahnung sagt Dir, dass der Trank irgendeine positive " |
| "Wirkung hat.")); |
| } |
| |
| // ggf. an die Hooks registrieren, wenn noch Schutzwirkung uebrig ist. |
| if (prot_poison > 0) |
| { |
| // Rueckgabewerte von HRegisterToHook speichern... |
| int *res = ({ reg_hook(H_HOOK_POISON, H_DATA_MODIFICATOR) }); |
| res += ({ reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR) }); |
| // Wenn alle versuchten Registrierungen erfolgreich waren...? Ansonsten |
| // andere Meldung... Ich bin noch nicht gluecklich, das hier so explizit |
| // auszugeben, aber ich weiss gerade sonst nicht, wie man drauf kommen |
| // soll, dass es ein Problem gibt. |
| if (sizeof(res) == sizeof(res & ({1}))) |
| tell_object(environment(), |
| "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n"); |
| else |
| { |
| // zumindest ein erfolg? |
| if (member(res, 1) > -1) |
| tell_object(environment(), |
| "Vielleicht haelt ein Teil dieser Wirkung ja sogar noch etwas an?\n"); |
| } |
| } |
| return 1; |
| } |
| |
| private int act_attr_heal_disease(int effekt) |
| { |
| int erfolgreich; |
| |
| // max. 40 Krankheitslevel heilen... |
| prot_disease = effekt / 50; |
| |
| if (prot_disease > 0) |
| { |
| tell_object(environment(), break_string( |
| "Du fuehlst, wie der Trank in Deinem Bauch eine wohlige Waerme " |
| "verbreitet und laechelst unwillkuerlich.",78)); |
| |
| // Objekte suchen. |
| object *ob = filter_objects(all_inventory(environment()), |
| "is_class_member", CL_DISEASE); |
| foreach(object o: ob) |
| { |
| // Heilen kostet ein Fuenftel des Levels, aber min. 1. |
| int disease = o->QueryProp(P_LEVEL); |
| if (disease <= prot_disease*5) |
| { |
| prot_disease -= disease/5 + 1; |
| o->SetProp(P_LEVEL, 0); |
| if (o->remove()) |
| ++erfolgreich; |
| } |
| else |
| { |
| disease -= prot_disease * 5; |
| prot_disease = 0; |
| o->SetProp(P_LEVEL, disease); |
| if (o->QueryProp(P_LEVEL) == disease) |
| ++erfolgreich; |
| } |
| if (prot_disease <= 0) |
| break; |
| } |
| } |
| else |
| { |
| tell_object(environment(), BS( |
| "Der Trank schmeckt eklig faulig. Dein Magen rebelliert umgehend. " |
| "Du kannst Deinen Brechreiz gerade noch unterdruecken, fuehlst " |
| "Dich aber krank.")); |
| call_out(#'ticktack, 5, effekt, 0); // 0 => Krankheit |
| return 1; |
| } |
| |
| if (erfolgreich) |
| { |
| tell_object(environment(), break_string( |
| "Entspannt durchatmend stellst Du fest, dass Du Dich viel besser fuehlst - der " |
| "Trank wirkt offenbar gegen Krankheiten.",78)); |
| } |
| else |
| { |
| tell_object(environment(), break_string( |
| "Eine Ahnung sagt Dir, dass der Trank irgendeine positive " |
| "Wirkung hat.")); |
| } |
| |
| // ggf. an die Hooks registrieren. |
| if (prot_disease > 0) |
| { |
| // Registrierung erfolgreich...? Ansonsten andere Meldung... Ich bin |
| // noch nicht gluecklich, das hier so explizit auszugeben, aber ich |
| // weiss gerade sonst nicht, wie man drauf kommen soll, dass es ein |
| // Problem gibt. |
| if (reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR)==1) |
| tell_object(environment(), |
| "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n"); |
| |
| } |
| return 1; |
| } |
| |
| private string num2desc_fight(int bumms) |
| { |
| switch(abs(bumms)) |
| { |
| case 0..499: |
| return "ein wenig"; |
| case 500..999: |
| return "spuerbar"; |
| case 1000..1499: |
| return "deutlich"; |
| case 1500..2000: |
| return "erheblich"; |
| } |
| return "ungenehmigt viel"; // kommt hoffentlich nicht vor. |
| } |
| |
| // AN: Tiere sind: CL_ANIMAL, CL_FISH, CL_FROG, CL_INSECT, CL_MAMMAL, |
| // CL_MAMMAL_LAND, CL_MAMMAL_WATER, CL_REPTILE, CL_ARACHNID, CL_BIRD |
| private int act_attr_dam_animals(int effekt) |
| { |
| if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1) |
| { |
| if (!mappingp(att_classes)) att_classes=m_allocate(1); |
| att_classes[CL_ANIMAL] = effekt/20; |
| tell_object(environment(), break_string( |
| "Du spuerst in Dir ein seltsames Verlangen aufsteigen, auf die Jagd " |
| "zu gehen - als wuerde Artemis persoenlich Deine Angriffe " |
| + num2desc_fight(effekt) |
| + " verbessern.",78)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| // AN: Magische Wesen sollen sein: CL_ELEMENTAL, CL_ILLUSION, CL_SHADOW |
| // CL_DRAGON, CL_DEMON, CL_SHAPECHANGER, CL_HARPY |
| private int act_attr_dam_magical(int effekt) |
| { |
| if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1) |
| { |
| if (!mappingp(att_classes)) att_classes=m_allocate(4); |
| att_classes[CL_DRAGON] = att_classes[CL_ELEMENTAL] |
| = att_classes[CL_SHADOW] |
| = att_classes[CL_ILLUSION] |
| = effekt/20; |
| tell_object(environment(), break_string( |
| "Merkwuerdig. Du hast gerade das Gefuehl, als fiele Dir der " |
| "Kampf gegen von Hekate beschenkte Wesen " |
| + num2desc_fight(effekt) |
| + " leichter.",78)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| // AN: Untote sollen sein: CL_SKELETON, CL_GHOUL, CL_GHOST, CL_VAMPIRE |
| // CL_ZOMBIE, CL_UNDEAD |
| // Bloed ist nur, dass CL_UNDEAD alle anderen enthaelt bis auf CL_GHOST. |
| // Also lassen wir die Unterscheidung und schrittweise Freischaltung |
| // erst einmal sein. |
| private int act_attr_dam_undead(int effekt) |
| { |
| if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1) |
| { |
| // Zombies, Skelette, Ghule... |
| if (!mappingp(att_classes)) att_classes=m_allocate(1); |
| att_classes[CL_UNDEAD] = effekt/20; |
| tell_object(environment(), break_string( |
| "Auf einmal hast Du den Eindruck, dass die Kreaturen des " |
| "Hades Deinen Angriffen " |
| + num2desc_fight(effekt) |
| + " weniger entgegen zu setzen haben.",78)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| private int act_attr_prot_animals(int effekt) |
| { |
| if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1) |
| { |
| if (!mappingp(prot_classes)) prot_classes=m_allocate(1); |
| prot_classes[CL_ANIMAL] = effekt/20; |
| tell_object(environment(), break_string( |
| "Du hast das Gefuehl, dass Artemis ihre schuetzende Hand " |
| + num2desc_fight(effekt) |
| + " ueber Dich haelt.",78)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| private int act_attr_prot_magical(int effekt) |
| { |
| if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1) |
| { |
| if (!mappingp(prot_classes)) prot_classes=m_allocate(4); |
| prot_classes[CL_DRAGON] = prot_classes[CL_ELEMENTAL] |
| = prot_classes[CL_SHADOW] |
| = prot_classes[CL_ILLUSION] |
| = effekt/20; |
| tell_object(environment(), break_string( |
| "Du hast das Gefuehl, dass von Hekate beschenkte Wesenheiten Dir " |
| +num2desc_fight(effekt) |
| + " weniger anhaben koennen.",78)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| private int act_attr_prot_undead(int effekt) |
| { |
| if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1) |
| { |
| // Zombies, Skelette, Ghule... |
| if (!mappingp(prot_classes)) prot_classes=m_allocate(1); |
| prot_classes[CL_UNDEAD] = effekt/20; |
| tell_object(environment(), break_string( |
| "Du bist ploetzlich zuversichtlich, Angriffen der Kreaturen " |
| "des Hades " |
| + num2desc_fight(effekt) |
| + " besser widerstehen zu koennen.",78)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| private int act_attr_tragen(int effekt) |
| { |
| if ( IS_LEARNER(environment()) ) |
| tell_object(environment(), sprintf("effekt: %d\n",effekt)); |
| SetProp(P_WEIGHT, QueryProp(P_WEIGHT) - effekt*4); |
| tell_object(environment(), |
| BS("Du fuehlst Dich ploetzlich "+num2desc(effekt) |
| + (effekt>0 ? " entlastet" : " belastet") + ".")); |
| return 1; |
| } |
| |
| // Berechnet eine max. Verzoegerung der Wirkung abhaengig von der Wirkung und |
| // einer Wirkschwelle. Der rel. Abstand von der Wirkschwelle (relativ zum max. |
| // moeglichen Abstand) wird hierbei genutzt. Ausserdem ist die max. |
| // Verzoegerung natuerlich die Wirkungsdauer des Trankes. |
| // <duration> muss im Trank schon gesetzt sein. |
| private int calc_max_delay(int effekt, int wirkschwelle) |
| { |
| int abstand = abs(effekt - wirkschwelle); |
| if (!duration) duration = time()+600; |
| if ( IS_LEARNER(environment()) ) |
| printf("calc_max_delay: %d\n",((duration-time()) * abstand) / |
| (2000-abs(wirkschwelle))); |
| return ((duration-time()) * abstand) / (2000-abs(wirkschwelle)); |
| } |
| |
| //TODO: die Zeitverzoegerung ist nen netter Effekt, aber zeitvergzoegerte |
| //Tports sind oefter keine gute Idee -> noch pruefen |
| //TODO: Die Zeitverzoegerung bei NO_TPORT_OUT auch nochmal pruefen |
| private int act_attr_flee_tport(int effekt) |
| { |
| // effekt > 0 teleportiert sofort zu halbwegs sicheren Orten |
| // -750 <= effekt < 0 teleportiert auch sofort zu nicht so dollen Orten |
| // -2000 <= effekt < -750 teleportiert spaeter zu bloeden Orten |
| if (effekt < -750 && effekt >= -2000) |
| { |
| // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem |
| // effekt rufen, der eine instantane Reaktion ausloest. |
| // effekt - 2000 ist ein Hack, damit nicht nochmal verzoegert wird. |
| call_out(#'act_attr_flee_tport, |
| random(calc_max_delay(effekt, -750))+1, effekt - 2000); |
| tell_object(environment(), |
| "Deine Umgebung fuehlt sich nach und nach unwirklicher an.\n"); |
| return 1; |
| } |
| |
| // Effekte instantan ausloesen. |
| |
| // wenn hier kein P_NO_TPORT erlaubt ist, mal gucken, ob wir spaeter noch |
| // zeit haben. |
| if (environment(environment())->QueryProp(P_NO_TPORT)==NO_TPORT_OUT) |
| { |
| tell_object(environment(), |
| BS("Irgendetwas haelt Dich an diesem Ort fest.")); |
| int delay = duration - time(); |
| // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen. |
| if (delay>10) |
| { |
| // AN/TODO: Kann das nicht auch ziemlich lang sein? |
| call_out(#'act_attr_flee_tport, random(delay), effekt); |
| } |
| return 0; |
| } |
| |
| // den Hack von oben rueckgaengig machen, wir brauchen den originalen |
| // Effektwert zurueck. |
| if (effekt < -2000) |
| effekt += 2000; |
| |
| string* dests; |
| if ( effekt > 0 ) |
| { |
| switch(effekt) |
| { |
| case 0..499: |
| dests = ({"/d/inseln/zesstra/vulkanweg/room/r1", |
| "/d/fernwest/li/xian/lab2/grab2", |
| "/d/ebene/miril/schloss/heide11", |
| "/d/polar/alle/room/weg4_15", |
| "/d/dschungel/arathorn/tempel/room/t4-5", |
| "/d/anfaenger/arathorn/minitut/room/huette_"+ |
| environment()->query_real_name(), |
| }); |
| break; |
| case 500..749: |
| dests = ({"/d/ebene/bertram/ebene/wasser8", |
| "/d/ebene/room/gebuesch2_3", |
| "/d/polar/alle/room/eiswueste/eiswueste[4,6]", |
| }); |
| if (environment()->QueryProp(P_REAL_RACE)!="Dunkelelf") |
| dests += ({"/d/unterwelt/padreic/kneipe/kneipe"}); |
| break; |
| case 750..999: |
| dests = ({"/d/gebirge/silvana/cronoertal/room/tf4", |
| "/d/inseln/schiffe/floss", |
| "/d/polar/humni/hexen/room/leuchtkammer", |
| "/d/polar/gabylon/temong/rooms/anlegestelle", |
| }); |
| break; |
| case 1000..1249: |
| dests = ({"/d/fernwest/shinobi/konfu_quest/room/insel4", |
| "/d/fernwest/ulo/mura/tokoro/haus4", |
| "/d/ebene/room/Halle/shalle14", |
| }); |
| break; |
| case 1250..1499: |
| dests = ({"/d/gebirge/silvana/cronoertal/room/tf4", |
| "/gilden/zauberer", |
| "/d/gebirge/georg/room/w11" |
| }); |
| break; |
| case 1500..1749: |
| dests = ({"/gilden/bierschuettler", |
| "/gilden/kaempfer", |
| "/d/wald/gundur/hobbitdorf/schrein", |
| "/d/vland/morgoth/room/city/rathalle", |
| }); |
| break; |
| default: |
| dests = ({environment()->QueryProp(P_START_HOME)|| |
| environment()->QueryDefaultHome(), |
| environment()->QueryDefaultHome(), |
| "/d/ebene/room/PortVain/po_haf2", |
| "/d/gebirge/room/he3x3", |
| "/d/ebene/room/huette", |
| }); |
| break; |
| } |
| } |
| else if ( effekt < 0 ) |
| { |
| switch(effekt) |
| { |
| case -499..0: |
| dests = ({"/d/polar/bendeigid/rooms/neskaya/neskay12", |
| "/players/ketos/gl/room/gl1x1", |
| "/d/inseln/zesstra/vulkanweg/room/r10", |
| "/d/vland/morgoth/room/kata/ktanke", |
| "/d/ebene/zardoz/burg/feuerrau", |
| }); |
| break; |
| case -999..-500: |
| dests = ({"/d/ebene/room/Hoehle/hg6", |
| "/d/gebirge/silvana/warok/room/l3/wa_3x7", |
| "/d/vland/morgoth/room/kata/xkat03", |
| "/d/vland/morgoth/room/kata/kata5", |
| "/d/wald/yoru/ort/dravi/weg5", |
| "/d/wald/atamur/room/e83", |
| }); |
| break; |
| case -1499..-1000: |
| dests = ({"/d/polar/bendeigid/rooms/pass/pass_e1", |
| "/d/ebene/room/gebuesch", |
| "/d/gebirge/silvana/warok/room/l1/wa_1x6", |
| "/d/vland/morgoth/room/kata/gkat17", |
| "/d/wald/atamur/room/e12", |
| }); |
| if (environment()->QueryProp(P_REAL_RACE)=="Dunkelelf") |
| dests += ({"/d/unterwelt/padreic/kneipe/kneipe"}); |
| break; |
| case -1749..-1500: |
| dests = ({"/d/dschungel/wurzel/t2", |
| "/d/vland/morgoth/room/kata/ukat26", |
| "/d/vland/morgoth/room/kata/h12", |
| "/d/gebirge/boing/sm/l5/m5x2", |
| }); |
| if (environment()->QueryProp(P_GUILD)=="chaos") |
| dests+=({"/gilden/klerus"}); |
| break; |
| default: |
| dests = ({"/d/ebene/rochus/room/sp10", |
| "/d/ebene/rochus/quest_3player/room/schacht10", |
| }); |
| if ( IS_SEER(environment()) ) |
| dests += ({"/d/wald/paracelsus/froom/sch_6e", |
| "/d/wald/paracelsus/froom/sch_9x", |
| "/d/wald/paracelsus/froom/sch2_6d", |
| }); |
| break; |
| } |
| } |
| tell_object(environment(), |
| BS("Eine Kraft zerrt an Dir, die Welt verschwimmt...")); |
| if (environment()->move(dests[random(sizeof(dests))],M_TPORT) == MOVE_OK) |
| tell_object(environment(), |
| "Einen Moment spaeter bist Du ganz woanders.\n"); |
| else |
| tell_object(environment(), |
| "Aber sonst passiert nichts.\n"); |
| return 1; |
| } |
| |
| private int act_attr_change_dimension(int effekt) |
| { |
| // nur effekt >= 1000 aendern die Dimension instantan. ;-) |
| if (effekt > 0 && effekt < 1000) |
| { |
| // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem |
| // effekt rufen, der eine instantane Reaktion ausloest. |
| call_out(#'act_attr_change_dimension, |
| random(calc_max_delay(effekt,1000))+1, 1000); |
| tell_object(environment(),BS("Um Dich herum wird alles " |
| "langsam grauer.\n")); |
| return 1; |
| } |
| // nur -600 < effekt < 0 aendern die Dimension instantan ;-) |
| if (effekt < -600) |
| { |
| // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem |
| // effekt rufen, der eine instantane Reaktion ausloest. |
| call_out(#'act_attr_change_dimension, |
| random(calc_max_delay(effekt, -600))+1, -601); |
| tell_object(environment(),BS("Um Dich herum wird alles " |
| "langsam grauer.\n")); |
| return 1; |
| } |
| // Effekte instantan ausloesen. |
| // wenn hier kein Para-Trans erlaubt ist, mal gucken, ob wir spaeter noch |
| // zeit haben. |
| if (environment(environment())->QueryProp(P_NO_PARA_TRANS)) |
| { |
| int delay = duration - time(); |
| // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen. |
| if (delay>10) |
| { |
| call_out(#'act_attr_change_dimension, random(delay), effekt); |
| tell_object(environment(), BS("Die Welt um Dich wird ein " |
| "wenig grauer.")); |
| } |
| return 0; |
| } |
| if ( effekt > 0 ) |
| { |
| if ( environment()->QueryProp(P_PARA) > 0 ) |
| { |
| environment()->SetProp(P_PARA, 0); |
| tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur " |
| "ein graues Wabern um Dich herum, bevor die Welt wieder " |
| "normal aussieht.\n")); |
| } |
| else |
| { |
| tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um " |
| "Dich herum grau aus.")); |
| } |
| } |
| else if ( effekt < 0 ) |
| { |
| if ( !environment()->QueryProp(P_PARA) ) |
| { |
| environment()->SetProp(P_PARA, 1); |
| tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur " |
| "ein graues Wabern um Dich herum, bevor die Welt wieder " |
| "normal aussieht. Aber es bleibt ein ungutes Gefuehl.\n")); |
| } |
| else { |
| tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um " |
| "Dich herum grau aus.")); |
| } |
| } |
| return 1; |
| } |
| |
| private int act_attr_defrog(int effekt) |
| { |
| // nur effekt > 1000 entfroscht instantan. ;-) |
| if (effekt > 0 && effekt < 1000) |
| { |
| // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem |
| // effekt rufen, der eine instantane Reaktion ausloest. |
| call_out(#'act_attr_defrog, random(calc_max_delay(effekt,1000))+1, 1000); |
| tell_object(environment(),BS( |
| "Du hoerst ploetzlich lautes Gequake, was langsam immer leiser " |
| "wird.\n")); |
| return 1; |
| } |
| // nur -500 < effekt < 0 froscht instantan ;-) |
| if (effekt < -500) |
| { |
| // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem |
| // effekt rufen, der eine instantane Reaktion ausloest. |
| call_out(#'act_attr_defrog, random(calc_max_delay(effekt, -500))+1, -501); |
| tell_object(environment(),BS( |
| "Du hoerst ploetzlich ganz leisess Gequake, was langsam immer lauter " |
| "wird.\n")); |
| return 1; |
| } |
| |
| // Effekte instantan ausloesen. |
| if ( effekt > 0 ) |
| { |
| if ( environment()->QueryProp(P_FROG) ) |
| { |
| environment()->SetProp(P_FROG,0); |
| tell_object(PL, "Du fuehlst Dich deutlich weniger gruen.\n"); |
| } |
| else |
| { |
| tell_object(environment(), break_string("Die Welt um Dich herum verliert " |
| "ploetzlich alle gruenen Farbtoene, die bald darauf allmaehlich " |
| "zurueckkehren.",78)); |
| } |
| } |
| else if ( effekt < 0 ) { |
| if ( !environment()->QueryProp(P_FROG) ) { |
| environment()->SetProp(P_FROG, 1); |
| tell_object(environment(), "Quak!\n"); |
| } |
| else { |
| tell_object(environment(), break_string("Deine Sicht verschwimmt, alles wird " |
| "intensiv gruen. Merkwuerdig. Zum Glueck ist das schnell wieder " |
| "vorbei.",78)); |
| } |
| } |
| return 1; |
| } |
| |
| private int act_attr_heal_lp(int effekt) |
| { |
| if (effekt > 0) |
| { |
| tell_object(environment(), |
| BS("Du fuehlst Dich schlagartig "+num2desc(effekt) |
| + " besser.")); |
| environment()->restore_hit_points(effekt/10); |
| } |
| else |
| { |
| tell_object(environment(), |
| BS("Du fuehlst Dich schlagartig "+num2desc(effekt) |
| + " schlechter.")); |
| environment()->do_damage(-effekt/10); |
| } |
| return 1; |
| } |
| |
| private int act_attr_heal_sp(int effekt) |
| { |
| if (effekt > 0) |
| { |
| tell_object(environment(), |
| BS("Du fuehlst Dich schlagartig "+num2desc(effekt) |
| + " konzentrierter.")); |
| environment()->restore_spell_points(effekt/10); |
| } |
| else |
| { |
| tell_object(environment(), |
| BS("Du fuehlst Dich schlagartig "+num2desc(effekt) |
| + " unkonzentrierter.")); |
| environment()->reduce_spell_points(-effekt/10); |
| } |
| return 1; |
| } |
| |
| |
| private int modify_sa(string sa, int effekt) |
| { |
| string msg; |
| switch (sa) |
| { |
| case SA_SPEED: |
| msg = "Du wirst gerade " + num2desc(effekt) |
| +(effekt>0 ? " schneller." : " langsamer."); |
| effekt = (effekt * 30) / 2000; |
| break; |
| case SA_DURATION: |
| msg = "Du wirst gerade " + num2desc(effekt) |
| +(effekt>0 ? " ausdauernder." |
| : " weniger ausdauernd."); |
| effekt = (effekt * 25) / 2000; |
| break; |
| case SA_SPELL_PENETRATION: |
| msg = "Deine geistige Durchsetzungskraft hat sich gerade " |
| + num2desc(effekt) |
| +(effekt>0 ? " verbessert." : " verschlechtert."); |
| effekt = (effekt * 30) / 2000; |
| break; |
| } |
| if (environment()->ModifySkillAttribute(sa, effekt, duration-time()) == |
| SA_MOD_OK) |
| { |
| tell_object(environment(),BS(msg)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| private int act_attr_change_sa_speed(int effekt) |
| { |
| return modify_sa(SA_SPEED, effekt); |
| } |
| private int act_attr_change_sa_duration(int effekt) |
| { |
| return modify_sa(SA_DURATION, effekt); |
| } |
| private int act_attr_change_sa_spell_penetration(int effekt) |
| { |
| return modify_sa(SA_SPELL_PENETRATION, effekt); |
| } |
| |
| |
| // Spieler MUSS das environment() sein! |
| private void effekt() |
| { |
| // Als erstes die Wirkungsdauer verwursten, einige Effekte brauchen |
| // <duration>. |
| // Wann laufen die Effekte denn spaetenstens ab? |
| duration = time() + data[T_EFFECT_DURATION]; |
| call_out(#'terminate_effects, data[T_EFFECT_DURATION]); |
| |
| // nur echte wirkungen beruecksichtigen, keine "Metadaten" |
| mapping effects = data & T_KRAUT_EFFECTS; |
| // fuer Spieler wird der Paratrans nicht stattfinden. |
| if (!IS_SEER(environment())) |
| m_delete(data, T_CHANGE_DIMENSION); |
| |
| // Waehrend der Wirkung ist dieses Objekt schonmal unsichtbar. |
| SetProp(P_INVIS, 1); |
| // Gewicht nullen, bevor die Wirkungen aktiviert werden, da die |
| // Wirkung von T_CARRY ansonsten wieder deaktiviert wuerde. |
| SetProp(P_WEIGHT, 0); |
| // neue, leere Flasche ins Inventar des Spielers bewegen. |
| clone_object(TRANKITEM)->move(environment(),M_NOCHECK|M_SILENT); |
| |
| // auftrennen in positive und negative Effekte. Keys mit Wert 0 werden |
| // ignoriert. |
| mapping peff = filter(effects, function int (string k, int val) |
| {return val > 0;}); |
| mapping neff = filter(effects, function int (string k, int val) |
| {return val < 0;}); |
| // erst die positiven, dann die negativen Wirkungen aktivieren |
| // fuer jede Wirkung wird eine lfun act_<trankattribut>() gerufen, z.B. |
| // act_attr_tragen() (-> act_T_CARRY() ) |
| mapping notactivated = |
| filter(peff, function int (string k, int val) |
| {return !funcall(symbol_function("act_"+k,this_object()), val);}) |
| + |
| filter(neff, function int (string k, int val) |
| {return !funcall(symbol_function("act_"+k,this_object()), val);}); |
| // Meldungen ausgeben ueber nicht aktivierte Wirkungen? |
| // TODO |
| } |
| |
| static int cmd_trinken(string str, mixed *param) |
| { |
| if (environment(this_object()) != PL) |
| { |
| write("Aus auf dem Boden liegende Flaschen trinkt es sich so " |
| "schlecht!\n"); |
| } |
| else if (data==0) |
| { |
| write("Die Flasche ist leer, Du muesstest erst etwas hineinfuellen.\n"); |
| } |
| else if (time()>expiry) |
| { |
| write(BS("Irgendwie passiert nichts. Hattest Du vielleicht doch nur " |
| "klares Wasser abgefuellt?")); |
| data=0; |
| } |
| else { |
| // Die Sperrzeit muss hier separat berechnet werden, weil eine |
| // Anpassung der Wirkungsdauer im Krautmaster dazu fuehren wuerde, |
| // dass es keine instantan wirkenden Traenke mehr gaebe. |
| int blocktime = max(60+random(60), data[T_EFFECT_DURATION]); |
| if(environment()->check_and_update_timed_key(blocktime, DRINK_POTION)==-1) |
| { |
| write(BS("Du oeffnest die Flasche und trinkst ihren Inhalt aus.")); |
| say(BS(PL->Name(WER, 0)+" oeffnet eine Flasche und trinkt sie in einem " |
| "Schluck aus.")); |
| effekt(); |
| // TODO: reicht das hier aus, oder muss das noch an anderen Stellen |
| // eingebaut werden? |
| RemoveId(({"trank","kraeutertrank"})); |
| } |
| else { |
| tell_object(environment(), BS( |
| "Der letzte Trank wirkt noch ein wenig nach. Du kannst Deinem " |
| "Magen nicht so bald schon den naechsten zumuten.")); |
| } |
| } |
| return 1; |
| } |
| |
| public nomask int Fill(object *plants) |
| { |
| if (!pointerp(plants)) return -1; |
| if (data) return -2; // schon voll |
| data = PLANTMASTER->make_potion(plants); |
| AddId(({"trank","kraeutertrank"})); |
| if (mappingp(data)) |
| { |
| // Pflanzen zerstoert der Master, wenns geklappt hat. |
| expiry=time()+data[T_EXPIRE]; |
| SetProp(P_WEIGHT, 300); |
| return 1; |
| } |
| return -3; |
| } |
| |
| void NotifyPlayerDeath(object vic, object killer, int exp) |
| { |
| call_out("remove",1); |
| } |