| /* |
| Zusammenfassung Bedingungen: |
| - Angel auswerfen |
| - Haken muss dran sein |
| - Haken muss Koeder haben |
| - darf nur ein Haken drin sein, sonst nix, und genau ein Haken |
| - Angel im Inventar |
| - Angel darf nicht schon ausgeworfen sein |
| - passendes Gewaesser im Raum |
| - Move-Hook muss korrekt registrierbar sein |
| - Angel einholen |
| - Angel-Callout muss ueberhaupt erstmal laufen |
| - Waehrend des Angelns |
| - bewegen beendet das Angeln |
| - Ausloggen beendet das Angeln |
| - Ende beendet das Angeln |
| - nichts rausnehmen |
| - nichts reintun, ausser Fisch am Ende der Wartezeit |
| - Langbeschreibung zeigt "ausgeworfen", Koeder und gefangenen Fisch an |
| - Fisch fangen (call_out zuende) |
| - Gewaesser passt (immer noch) |
| - Fisch passt noch in die Angel rein |
| - Haken dranhaengen |
| - Haken im Inv |
| - Haken muss "echter" Angelhaken sein |
| - Angel im Inv |
| - Koeder am Haken |
| - noch kein anderer Koeder dran |
| - Angel ist nicht ausgeworfen |
| - generell |
| - nur Haken dranstecken, nix anderes reintun |
| - reintun nur mit Syntax "haenge" etc. (damit sind Fische aussen vor, |
| weil die Syntax auf Haken prueft) |
| (ggf. ist dann das PreventInsert() zu vereinfachen) |
| */ |
| |
| #pragma strong_types, save_types, rtt_checks |
| #pragma no_clone, no_shadow |
| |
| inherit "/std/container"; |
| inherit "/std/hook_consumer"; |
| |
| #include <new_skills.h> |
| #include <language.h> |
| #include <properties.h> |
| #include <moving.h> |
| #include <hook.h> |
| #include <wizlevels.h> |
| #include <items/fishing/fishing.h> |
| #include <items/fishing/aquarium.h> |
| |
| #define TP this_player() |
| #define ME this_object() |
| #define BS(x) break_string(x, 78) |
| |
| #define MIN_DELAY 80 // Mindestdelay bis Fisch beisst! |
| #define STD_DELAY_PLUS 60 // +/- 60 sekunden Tolerant |
| #define MAX_FISH_BONUS 60 // max. Wartezeitverkuerzung durch P_FISH |
| #define WRONGWATER 60 // 1-2 min, falls Angel nicht geeignet |
| #define WRONGWORM 60 // 1-2 min, falls Koeder nicht geeignet |
| |
| #define KOEDER_LOST 10 |
| // Verlustwahrsch. fuer Koeder falls Fisch zu schwer |
| |
| private object room, current_user; |
| private int active, actime; |
| private mapping aquarium = STDFISHLIST; |
| |
| nomask varargs void StopFishing(string msg_me, string msg_room); |
| int IsFishing(); |
| |
| protected void create() { |
| ::create(); |
| |
| SetProp(P_SHORT, "Eine Angel"); |
| SetProp(P_LONG, "Eine nagelneue Angel.\n"); |
| SetProp(P_NAME, "Angel"); |
| AddId(({"angel", "rute", ANGEL_ID})); |
| |
| SetProp(P_GENDER, FEMALE); |
| SetProp(P_MATERIAL,([MAT_MISC_WOOD:90,MAT_WOOL:10])); |
| SetProp(P_TRANSPARENT,1); |
| SetProp(P_WEIGHT,200); |
| SetProp(P_MAX_WEIGHT, 10000); |
| SetProp(P_WATER, W_USER); //muss (mehrere) Flags gesetzt haben! |
| SetProp(P_SOURCE_PREPOSITION, "von"); |
| SetProp(P_NR_HANDS, 2); |
| |
| Set(P_NODROP, function string () { |
| return (IsFishing()?"Du solltest die Angel nicht fallenlassen, solange " |
| "Du angelst.\n":0); |
| }, F_QUERY_METHOD); |
| |
| AddCmd(({"angel","angle"}),"angel"); |
| AddCmd("hole|hol&@ID&ein", "stopit", "Was willst Du (ein)holen?|" |
| "Willst Du @WEN2 einholen?"); |
| AddCmd("haenge|haeng|befestige&@PRESENT&@ID", "move_in", |
| "Was willst Du woran @verben?|" |
| "Woran willst Du @WEN2 denn @verben?"); |
| |
| AddCmd("atell","tell_stat"); |
| AddCmd("angeltest","qangel"); |
| } |
| |
| protected void create_super() { |
| set_next_reset(-1); |
| } |
| |
| static int stopit(string str) { |
| if( find_call_out("do_angel")==-1 ) |
| tell_object(TP, "Du angelst nicht!\nTP, "); |
| else |
| StopFishing(); |
| return 1; |
| } |
| |
| varargs string long(int mode) { |
| string descr = QueryProp(P_LONG); |
| if(!QueryProp(P_TRANSPARENT)) |
| return descr; |
| |
| if ( find_call_out("do_angel")!=-1 ) |
| descr += capitalize(QueryPronoun(WER)) + " ist ausgeworfen.\n"; |
| |
| object *inv = all_inventory(ME); |
| string inv_descr = make_invlist(TP, inv); |
| if ( inv_descr != "" ) |
| descr += "An "+QueryPronoun(WEM) + " haeng"+(sizeof(inv)>1?"en":"t")+ |
| ":\n" + inv_descr; |
| |
| return descr; |
| } |
| |
| /*int tell_stat() |
| { |
| int n; |
| |
| if(!IS_WIZARD(TP)) |
| return 0; |
| if(!active){ |
| write("Du angelst nicht\n"); return 1; |
| } |
| n=find_call_out("do_angel"); |
| if(active) |
| write( |
| "----------------------------------------------------------------------\n" |
| +"Der Fisch beisst in "+n+" sec!\n" |
| +"Bestandswert des Raumes: "+room->QueryProp(P_FISH) |
| +" = "+(room->QueryProp(P_FISH)+100)+"%.\n" |
| +"Bestandswert des Koeders: "+koeder->QueryProp(P_FISH) |
| +" = "+(100+koeder->QueryProp(P_FISH))+"%.\n" |
| +"Gesamt:"+bestand+" = "+(bestand+100)+"%.\n"); |
| if(!(QueryProp(P_WATER)&room->QueryProp(P_WATER))) |
| write("Die Angel passt nicht zum Gewaessertypen.\n"); |
| else |
| write("Die Angel passt zum Gewaessertypen.\n"); |
| if(!(room->QueryProp(P_WATER)&koeder->QueryProp(P_WATER))) |
| write("Der Koeder passt nicht zum Gewaessertypen.\n"); |
| else |
| write("Der Koeder passt zum Gewaessertypen.\n"); |
| write(" => Delay="+delay+".\n" |
| +"----------------------------------------------------------------------\n" |
| ); |
| return 1; |
| }*/ |
| |
| static int move_in(string str, mixed *param) { |
| object haken = param[0]; |
| // param[1] sind wir selbst, daher ab hier ME verwendet. |
| |
| if ( find_call_out("do_angel")!=-1 ) { // angelt noch nicht? |
| tell_object(TP, BS(Name(WER,1)+" ist bereits ausgeworfen, da haengst " |
| "Du jetzt besser nichts mehr dran, das vertreibt nur die Fische.")); |
| } |
| else if ( !haken->id(HAKEN_ID) ) { // echter Angelhaken? |
| tell_object(TP, BS(haken->Name(WER,1)+" ist kein Angelhaken, "+ |
| haken->QueryArticle(WEN, 1, 1)+"kannst Du nicht an "+name(WEN,1)+ |
| " haengen.")); |
| } |
| else if ( environment(haken) != TP ) { // Haken im Inv? |
| tell_object(TP, BS("Wie er da so auf dem Boden liegt, schaffst Du es " |
| "einfach nicht, "+haken->name(WEN,1)+" vernuenftig an "+name(WEM,1)+ |
| " zu befestigen.")); |
| } |
| else if ( environment(ME) != TP ) { // Angel im Inv? |
| tell_object(TP, BS("Es stellt sich viel fummeliger heraus als gedacht, "+ |
| haken->name(WEN,1)+" an "+name(WEM,1)+" zu befestigen, solange "+ |
| QueryPronoun(WER)+" auf dem Boden herumliegt.")); |
| } |
| else if ( present(HAKEN_ID, ME) ) { // anderer Haken schon dran? |
| tell_object(TP, BS("An "+name(WEM,1)+" haengt bereits ein Angelhaken.")); |
| } |
| else if ( !haken->QueryKoeder() ) { // Koeder am Haken? |
| tell_object(TP, BS("Du willst "+haken->name(WEN,1)+" gerade an "+ |
| name(WEN,1)+" haengen, als Dir auffaellt, dass Du beinahe den Koeder " |
| "vergessen haettest.")); |
| } |
| else { // dann darf der Haken rangehaengt werden. |
| if ( haken->move(ME, M_PUT) == MOVE_OK ) { |
| tell_object(TP, BS("Du haengst "+haken->name(WEN,1)+" an "+ |
| name(WEN,1)+".")); |
| tell_room(environment(TP), BS(TP->Name(WER)+" haengt etwas metallisch " |
| "Glaenzendes an "+name(WEN)+", vermutlich einen Angelhaken."), |
| ({TP})); |
| } |
| else { |
| tell_object(TP, BS(haken->Name(WER,1)+" laesst sich nicht an "+ |
| name(WEM,1)+" befestigen.")); |
| } |
| } |
| return 1; |
| } |
| |
| static int qangel() { |
| if(IS_WIZARD(TP)) { |
| call_out("do_angel",1); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int angel(string str) { |
| object haken = present(HAKEN_ID,ME); |
| if ( environment(ME) != TP ) { |
| tell_object(TP, BS("Dafuer musst Du die Angel in die Hand nehmen.")); |
| } |
| else if ( find_call_out("do_angel")!=-1 ) { |
| tell_object(TP, "Das tust Du doch schon.\n"); |
| } |
| else if(!objectp(haken)) { |
| tell_object(TP, "Wie soll das gehen? Du hast ja nichtmal einen Haken " |
| "an der Angel!\n"); |
| } |
| else if(!haken->QueryKoeder()) { |
| tell_object(TP, break_string("Ohne Koeder am Haken wird sich kein " |
| "Fisch der Welt fuer Deine Angel interessieren.",78)); |
| } |
| else if(present(FISCH_ID,ME)) { |
| tell_object(TP, "Nimm erst mal die Beute von der Angel, die noch daran " |
| "zappelt!\n"); |
| } |
| else if ( !TP->UseHands(ME, QueryProp(P_NR_HANDS)) ) { // freie Haende? |
| tell_object(TP, BS("Du musst schon beide Haende frei haben, um die " |
| "Angel sicher festhalten zu koennen.")); |
| } |
| else { |
| // Raum festhalten, in dem geangelt wird. |
| room = environment(TP); |
| // Gewaessertyp abfragen |
| active = room->QueryProp(P_WATER); |
| // Kein Wasser vorhanden, oder nicht-befischbarer Sondertyp? |
| if( (!active) || (active & W_OTHER) ) { |
| tell_object(TP, "Du kannst hier nicht angeln.\n"); |
| TP->FreeHands(ME); |
| } |
| // totes Gewaesser erwischt |
| else if ( active & W_DEAD ) { |
| write("In dem Gewaesser hier gibt es keine Fische.\n"); |
| TP->FreeHands(ME); |
| } |
| // Jetzt aber: es gibt Fisch, Baby. ;-) |
| else { |
| int delay = MIN_DELAY; // 80 Sekunden. |
| // Fischbestand ermitteln aus Raum- und Koederparametern |
| int bonus = room->QueryProp(P_FISH) + haken->QueryProp(P_FISH); |
| // Ist kein Bonus, sondern ein Malus rausgekommen? |
| if ( bonus < 0 ) { |
| // Dann begrenzen auf 5 Min. |
| if ( bonus < -300 ) |
| bonus = -300; |
| // Wartezeit erhoehen. |
| delay += random(-bonus); |
| } |
| else { |
| // Bonus deckeln auf Max-Wert (60 Sekunden) |
| if ( bonus > MAX_FISH_BONUS ) { |
| bonus = MAX_FISH_BONUS; |
| } |
| // Delay reduzieren (minimal 20 Sekunden) |
| delay -= random(bonus); |
| } |
| |
| // passt das Gewaesser zur Angel/zum Koeder ? |
| if( !(QueryProp(P_WATER) & room->QueryProp(P_WATER)) ) |
| delay += WRONGWATER + random(WRONGWATER); |
| if( !(room->QueryProp(P_WATER) & haken->QueryProp(P_WATER)) && |
| room->QueryProp(P_WATER) != W_USER ) |
| delay += WRONGWORM + random(WRONGWORM); |
| |
| int hook = TP->HRegisterToHook(H_HOOK_MOVE, ME, H_HOOK_OTHERPRIO(1), |
| H_LISTENER, 0); |
| if ( hook != 1 ) { |
| active = 0; |
| room = 0; |
| TP->FreeHands(ME); |
| tell_object(TP, BS( |
| "Du schleuderst die Angel mit Schwung in Richtung Wasser, aber der " |
| "Haken verfaengt sich in Deinen Sachen und piekst Dir in den " |
| "Allerwertesten. Das versuchst Du besser noch einmal anders.")); |
| tell_room(environment(TP), BS( |
| TP->Name()+" schleudert eine Angel in Richtung Wasser, ist dabei " |
| "aber so ungeschickt, dass sich der Haken verfaengt."), ({TP})); |
| } |
| else { |
| // Alle Bedingungen erfuellt: |
| // - Angel in der Hand. |
| // - Gewaesser stimmt. |
| // - Haken mit Koeder ist dran. |
| // - alte Beute wurde abgenommen. |
| // - Move-Hook gesetzt, um Abbruch ausloesen zu koennen. |
| actime = time(); |
| current_user = TP; |
| tell_object(TP, "Du wirfst die Angel aus.\n"); |
| tell_room(environment(TP), TP->Name()+" wirft eine Angel aus.\n", |
| ({TP})); |
| call_out("do_angel", delay); |
| } |
| } |
| } |
| return 1; |
| } |
| |
| private object GetFish(object room) { |
| int wtype = room->QueryProp(P_WATER); |
| string *fische; |
| |
| // Wenn GetAquarium() bei W_USER nichts zurueckliefert, geht der Angler |
| // leer aus. Es kann kein Fisch aus der Std-Liste genommen werden, weil |
| // der Gewaessertyp ja per Definition benutzerdefiniert ist. |
| if ( wtype == W_USER ) |
| fische = room->GetAquarium(ME)||({}); |
| else |
| fische = aquarium[wtype]; |
| |
| string beute = fische[random(sizeof(fische))]; |
| |
| if ( !beute ) |
| return 0; |
| // GetAquarium() liefert volle Pfade, STDFISHLIST nur Dateinamen relativ |
| // zu FISH() |
| else if ( wtype == W_USER ) |
| return clone_object(beute); |
| else |
| return clone_object(FISH(beute)); |
| } |
| |
| static void do_angel() { |
| object haken = present(HAKEN_ID, ME); |
| object room = environment(TP); |
| object fisch; |
| |
| string msg_self, msg_other; |
| if ( member(TP->QueryProp(P_HANDS_USED_BY), ME)==-1 ) { |
| tell_object(TP, BS("Waehrend Du vertraeumt vor Dich hingeangelt hast, " |
| "hat sich Dein Griff an der Angel wohl gelockert, so dass der " |
| "Fisch, der gerade anbeissen wollte, sie Dir beinahe entrissen " |
| "haette.")); |
| tell_room(environment(TP), BS(TP->Name(WER)+" hat sich gerade " |
| "ziemlich erschreckt, als "+TP->QueryPronoun(WEM)+" beinahe "+ |
| TP->QPP(ME, WEN, SINGULAR)+" "+Name(RAW)+" entrissen worden waere."), |
| ({TP})); |
| if ( !random(3) && haken->KoederGefressen() ) |
| tell_object(TP, BS("Der Koeder ist jedenfalls futsch.")); |
| return; |
| } |
| |
| if ( !objectp(haken) ) { |
| msg_self = "Huch, anscheinend hat sich in einem unbeobachteten " |
| "Moment der Angelhaken von der Angelschnur geloest. War der Knoten " |
| "doch nicht gut genug gesichert?"; |
| msg_other = TP->Name()+" zieht unglaeubig eine leere Angelschnur aus " |
| "dem Wasser."; |
| } |
| else if ( !objectp(fisch=GetFish(room)) || |
| active != room->QueryProp(P_WATER) ) |
| { |
| msg_self = "Anscheinend gibt es hier doch keine Fische. Du gibst " |
| "auf und holst "+name(WEN,1)+" wieder ein."; |
| // Leaken von Fischobjekten verhindern. |
| if (objectp(fisch)) |
| fisch->remove(); |
| } |
| else if ( fisch->move(ME) != MOVE_OK ) { |
| msg_self = fisch->Name(WER)+" biss an, war aber zu schwer fuer " |
| "Dich (oder Deine Angel). "+capitalize(fisch->QueryPronoun(WER))+ |
| " entgleitet Dir und plumpst zurueck ins Wasser!"; |
| |
| if( !(fisch->QueryProp(P_FISH) & F_NOTHUNGRY) ) { |
| haken->KoederGefressen(); |
| if(!random(KOEDER_LOST)) { |
| msg_self += " Dein Haken ist dabei leider abgerissen!"; |
| tell_room(environment(TP), BS(Name(WER,1)+" von "+TP->Name(WEM)+ |
| " wackelte sehr stark."), ({TP})); |
| haken->remove(); |
| } |
| } |
| fisch->remove(1); |
| } |
| else { |
| haken->KoederGefressen(); |
| } |
| StopFishing(msg_self, msg_other); |
| } |
| |
| varargs int remove(int silent) { |
| if ( find_call_out("do_angel")!=-1 ) |
| StopFishing(Name(WER,1)+" loest sich ploetzlich im Nichts auf."); |
| return ::remove(silent); |
| } |
| |
| // Jemand macht "ende" waehrend er angelt? |
| protected void NotifyMove(object dest, object oldenv, int method) { |
| if ( find_call_out("do_angel")!=-1 && oldenv == current_user && |
| dest->IsRoom() ) { |
| StopFishing("Du holst die Angel ein und legst sie beiseite."); |
| } |
| return ::NotifyMove(dest, oldenv, method); |
| } |
| |
| // Spieler loggt waehrend des Angelns aus? Dann angeln einstellen. |
| void BecomesNetDead(object pl) { |
| if ( find_call_out("do_angel")!=-1 && pl == current_user ) { |
| StopFishing("Angeln ist so eine beruhigende Freizeitbeschaeftigung! " |
| "Bevor Du mit der Angel in der Hand einschlaeft, holst Du sie " |
| "lieber schnell ein.", pl->Name()+" holt schnell noch die Angel ein, " |
| "bevor "+pl->QueryPronoun(WER)+" einschlaeft."); |
| } |
| } |
| |
| int PreventInsert(object ob) { |
| // Funktion wird aus einer Spielereingabe heraus gerufen |
| // Ich nehme hier nicht TP, weil der call_out("do_angel") die |
| // gefangenen Fische per move() in die Angel bewegt: Bei call_out()s wird |
| // TP durch die Kette weitergereicht, so dass dann hier keine |
| // Unterscheidung zwischen Spielereingabe und call_out() moeglich waere. |
| SetProp(P_NOINSERT_MSG, 0); |
| if ( stringp(query_verb()) && |
| member(({"haenge","haeng","befestige"}), query_verb()) == -1 ) { |
| SetProp(P_NOINSERT_MSG, BS(Name(WER,1)+" ist nur dafuer geeignet, " |
| "Angelhaken daranzuhaengen.")); |
| return 1; |
| } |
| if( ob->id(FISCH_ID) ) { |
| write("Etwas zappelt an Deiner Angel.\n"); |
| } |
| return ::PreventInsert(ob); |
| } |
| |
| // Wenn geangelt wird, nimmt hier niemand was raus. |
| public int PreventLeave(object ob, mixed dest) { |
| SetProp(P_NOLEAVE_MSG, 0); |
| if ( find_call_out("do_angel")!=-1 ) { |
| if ( objectp(TP) && ob->id(HAKEN_ID) ) |
| SetProp(P_NOLEAVE_MSG, BS("Der Haken ist gerade unter Wasser. Es " |
| "waere kontraproduktiv, ihn ausgerechnet jetzt abzunehmen.")); |
| return 1; |
| } |
| return ::PreventLeave(ob, dest); |
| } |
| |
| // Beendet das Angeln, indem Aktiv-Flag geloescht, Hook deregistriert |
| // und call_out() geloescht wird. Zusaetzlich werden die als Argument ueber- |
| // gebenen Meldungen ausgegeben, oder passende generische verwendet. |
| nomask varargs void StopFishing(string msg_me, string msg_room) { |
| active = 0; |
| while(remove_call_out("do_angel")!=-1); |
| object env = environment(ME); |
| if ( objectp(env) && env == current_user ) { |
| env->FreeHands(ME); |
| current_user = 0; |
| env->HUnregisterFromHook(H_HOOK_MOVE, ME); |
| tell_object(env, BS(msg_me||"Du holst Deine Angel wieder ein.")); |
| tell_room(environment(env), BS(msg_room|| |
| env->Name(WER)+" holt "+env->QueryPossPronoun(ME,WEN,SINGULAR)+" "+ |
| name(RAW)+" ein."), ({env})); |
| } |
| } |
| |
| // Diese Methode wird in jedem Hook-Konsumenten eines Hook-Providers |
| // aufgerufen, solange die Verarbeitung nicht vorher abgebrochen wurde. |
| // Dann jedoch wurde auch die Aktion (Bewegung) nicht ausgefuehrt, d.h. |
| // solange hier kein Event ankommt, wurde keine Bewegung ausgefuehrt und |
| // das Angeln kann weiterlaufen. |
| mixed HookCallback(object hookSource, int hookid, mixed hookData) { |
| if ( hookid == H_HOOK_MOVE && hookSource == current_user ) { |
| StopFishing("Deine Angelschnur ist zu kurz, um damit rumlaufen zu " |
| "koennen. Du holst die Angel wieder ein."); |
| } |
| return ({H_NO_MOD, hookData}); |
| } |
| |
| // Wird gerufen, wenn der Konsument von einem anderen mit hoeherer Prioritaet |
| // verdraengt wurde. |
| void superseededHook(int hookid, object hookSource) { |
| if ( hookid == H_HOOK_MOVE && hookSource == current_user ) { |
| StopFishing("Irgendetwas ist gerade passiert, das alle Fische " |
| "vertrieben hat. Du siehst sie unter der Wasseroberflaeche davon" |
| "flitzen. Ernuechtert holst Du Deine Angel wieder ein."); |
| } |
| } |
| |
| // Angelzustand abfragen. |
| int IsFishing() { |
| return (find_call_out("do_angel")!=-1); |
| } |
| |
| // Gewaessertyp abfragen, in dem gerade geangelt wird. |
| int query_active() { |
| return active; |
| } |