Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/items/fishing/angel.c b/std/items/fishing/angel.c
new file mode 100644
index 0000000..1b5709f
--- /dev/null
+++ b/std/items/fishing/angel.c
@@ -0,0 +1,510 @@
+/*
+ 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;
+}
diff --git a/std/items/fishing/fish.c b/std/items/fishing/fish.c
new file mode 100644
index 0000000..4209b35
--- /dev/null
+++ b/std/items/fishing/fish.c
@@ -0,0 +1,222 @@
+//Revision 1.1: QueryQuality() added, sollte aber noch verbessert werden!
+/*
+Letzte Aenderung: Vanion, 26.05.02
+ - Die Fische haben jetzt wieder einen Wert. Der Preis wird
+ von einem Master kontrolliert. Je mehr Fisch ein Spieler
+ angelt, desto billiger wird der Fisch.
+ - Gross-Kleinschreibung beim essen korrigiert.
+*/
+
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <defines.h>
+#include <items/fishing/fishing.h>
+
+#define TP this_player()
+#define BS(x) break_string(x, 78)
+
+private string eatmessage;
+string corpseobject = "/items/fishing/graeten";
+static int price_modifier;
+
+#define DECAYSTART 600 // beginnt nach 10 min zu faulen
+#define DECAYSTEP 120 // alle 2 Minuten wird er schlechter
+#define MAXQUALITY 4 // nach 4 Decays ist er giftig
+#define KILLFACTOR 5 // reduce_hit_points(STEPS*KILLFACTOR)
+#define HEALFACTOR 32 // 800 g = 25 LP
+#define MAXHEAL 40 // Kein Fisch bringt mehr als 40 LP
+#define MINHEAL 0 // oder weniger als 0
+#define FOODHEALFACTOR 3 // LP=LP + 1/3 des Gewichts
+
+void SetEatMessage(string str);
+void SetCorpseObject(string str);
+int QueryQuality();
+
+protected void create() {
+ ::create();
+ // Reset kurz nach Erreichen des max. Gammelzustands, dann zerstoeren.
+ set_next_reset(DECAYSTART+(MAXQUALITY+1)*DECAYSTEP);
+ AddId(({FISCH_ID ,"fisch"}));
+ SetProp(P_NAME, "Fisch" );
+ SetProp(P_GENDER , MALE);
+ SetProp(P_SHORT, "Ein Fisch" );
+ SetProp(P_LONG, "Ein stinknormaler Fisch.\n");
+ SetProp(P_MATERIAL, MAT_MISC_FOOD);
+ SetProp(P_NOBUY,1);
+ SetProp(P_WEIGHT, 500);
+ AddCmd("iss&@ID", "eatit", "Was willst Du essen?");
+
+ // Vanion, 26.05.02: Der Preis wird erst nach dem create() geholt,
+ // da das Gewicht dann schon gesetzt werden muss.
+ call_out("GetPriceModifier",0);
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+// Wert des Fisches ist abhaengig vom price_modifier und von P_VALUE
+static int _query_value() {
+ int value;
+
+ // Minimalpreis ist immer 1 Muenze. Damit man ihn loswird.
+ if(!price_modifier)
+ return 1;
+
+ value = Query(P_VALUE, F_VALUE);
+
+ if(!value)
+ return 0;
+
+ return (value*price_modifier)/100 ;
+}
+
+/*Eingebaut von Vanion, 26.05.02
+ Die Funktion holt den price_modifier aus dem Fish-Master.
+ Hierbei wird ein Wert zwischen 0 und 100 zurueckgegeben, der
+ den Prozentsatz des Wertes von P_VALUE angibt. 50 bedeutet, dass
+ der Fisch 50% seines Maximalwertes hat. */
+
+#define FISHMASTER "p/daemon/fishmaster"
+
+void GetPriceModifier() {
+ if (!this_player()) return;
+
+ // Jetzt wird der Preis fuer den Fisch bestimmt.
+ price_modifier = FISHMASTER->PriceOfFish(TP, ME);
+ return;
+}
+
+void SetEatMessage(string str) {
+ eatmessage=str;
+}
+
+void SetCorpseObject(string str) {
+ corpseobject=str;
+}
+
+int _set_weight(int gramm) {
+ int tmp = gramm/2 + random(gramm/2); // "nat. Schwankungen" nachbilden
+ Set(P_WEIGHT, tmp, F_VALUE);
+ SetProp(P_VALUE, tmp/4); // Im normalen Laden nur 1/4 Gewicht in Muenzen
+ return tmp;
+}
+
+void init() {
+ ::init();
+ if( QueryProp(P_FISH)&F_REPLACE ) {
+ if(query_once_interactive(environment(ME))) {
+ call_out("ReplaceFish",0);
+ }
+ }
+ return;
+}
+
+varargs string long(int mode) {
+ string pron = capitalize(QueryPronoun(WER));
+ string txt = QueryProp(P_LONG);
+
+ switch(QueryQuality()) {
+ case 4: txt += pron+" ist fangfrisch.\n"; break;
+ case 3: txt += pron+" ist noch recht frisch.\n"; break;
+ case 2: txt += pron+" ist schon etwas runzlig.\n"; break;
+ case 1: txt += pron+" sieht schon recht alt aus.\n"; break;
+ case 0: txt += pron+" ist nicht mehr geniessbar!\n"; break;
+ case -1: txt += pron+" ist vergammelt.\n"; break;
+ default:
+ txt += (pron+" stinkt widerlich und ist bereits voller Maden!\n"+pron+
+ " zerfaellt noch waehrend Du "+QueryPronoun(WEN)+" betrachtest.\n");
+ remove();
+ break;
+ }
+ return(txt);
+}
+
+int _query_fish_age() {
+ return (time()-QueryProp(P_CLONE_TIME));
+}
+
+static int eatit(string str) {
+ string msg_other = TP->Name()+" isst "+name(WEN)+".";
+
+ if ( !eatmessage )
+ eatmessage = "Du isst "+name(WEN,1)+" auf.";
+
+ if ( stringp(corpseobject) ) {
+ object muell=clone_object(corpseobject);
+ if ( muell->move(TP, M_GET) != MOVE_OK ) {
+ muell->move(environment(TP), M_GET);
+ }
+ }
+
+ // Heilung berechnen.
+ // Heilwert runterskalieren mit Qualitaet, wenn das Mindestalter erreicht
+ // ist, aber nur, wenn der Fisch auch vergammeln kann.
+ int healing = QueryProp(P_WEIGHT)/HEALFACTOR;
+ int age = QueryProp(P_FISH_AGE);
+ if ( age > DECAYSTART && !(QueryProp(P_FISH)&F_NOROTTEN)) {
+ healing = healing * (QueryQuality()*25)/100;
+ }
+ if ( healing > MAXHEAL )
+ healing = MAXHEAL;
+ else if ( healing < MINHEAL )
+ healing = MINHEAL;
+
+ if( age > DECAYSTART + MAXQUALITY*DECAYSTEP) {
+ tell_object(TP, BS(eatmessage + " Dir wird speiuebel, der Fisch war "
+ "zu alt!"));
+ tell_room(environment(TP), BS(msg_other + TP->QueryPronoun(WER)+
+ " verdreht angeekelt die Augen."), ({TP}));
+ TP->reduce_hit_points( ((age-DECAYSTART)/DECAYSTEP)*KILLFACTOR );
+ remove();
+ }
+ else {
+ if ( TP->eat_food(healing*FOODHEALFACTOR) ) {
+ tell_object(TP, BS(eatmessage));
+ tell_room(environment(TP), BS(msg_other), ({TP}));
+ if ( !(QueryProp(P_FISH)&F_NOHEAL) )
+ TP->buffer_hp(healing,5);
+ remove();
+ }
+ }
+ return 1;
+}
+
+// Qualitaet des Fisches ermitteln. Rueckgabewert: 4, 3, 2, 1, 0, -1 ...
+int QueryQuality() {
+ // Alter in Sekunden ueber der Grenze, ab der der Fisch anfaengt zu gammeln
+ int age = QueryProp(P_FISH_AGE)-DECAYSTART;
+ // Wenn die Grenze noch nicht erreicht ist, oder der Fisch generell nicht
+ // gammelt (F_NOROTTEN), dann ist das Alter 0 und die Qualitaet max.
+ if ( age<0 || QueryProp(P_FISH) & F_NOROTTEN )
+ age = 0;
+
+ // Qualitaet in DECAYSTEP-Stufen reduzieren (60 s pro Gammelstufe)
+ return MAXQUALITY-(age/DECAYSTEP);
+}
+
+void reset() {
+ if (clonep(ME)) {
+ string msg = BS(Name()+" ist vergammelt und zerfaellt.") ;
+ if ( interactive(environment()) ) {
+ tell_object(environment(), msg);
+ }
+ else if ( environment()->IsRoom() ) {
+ tell_room(environment(), msg);
+ }
+ if ( stringp(corpseobject) ) {
+ object muell=clone_object(corpseobject);
+ if ( muell->move(environment(), M_GET) != MOVE_OK ) {
+ muell->move(environment(environment()), M_GET);
+ }
+ }
+ call_out("remove",0);
+ return;
+ }
+ return ::reset();
+}
diff --git a/std/items/fishing/haken.c b/std/items/fishing/haken.c
new file mode 100644
index 0000000..66b91f4
--- /dev/null
+++ b/std/items/fishing/haken.c
@@ -0,0 +1,157 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_shadow, no_clone
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <items/fishing/fishing.h>
+#include <items/flasche.h>
+#include <unit.h>
+
+#define TP this_player()
+#define ME this_object()
+
+int QueryKoeder();
+
+protected void create() {
+ ::create();
+
+ AddId(({HAKEN_ID,"haken","angelhaken"}));
+ SetProp(P_NAME, "Haken");
+ SetProp(P_GENDER, MALE);
+ SetProp(P_ARTICLE, 1);
+ SetProp(P_WEIGHT, 5);
+ SetProp(P_SHORT, "Ein Angelhaken");
+ SetProp(P_LONG, "Ein Angelhaken aus Metall.\n");
+ SetProp(P_LONG_EMPTY,"Vielleicht kannst Du etwas damit aufspiessen?\n");
+
+ // Lang- und Kurzbeschreibungen reagieren auf einen evtl. vorhandenen
+ // Koeder, allerdings nur auf ein tatsaechlich existierendes Koeder-Objekt,
+ // nicht auf einen per ueberschriebenem QueryKoeder() vorgetaeuschten
+ // Koeder, wie es der Spezialhaken tut.
+ Set(P_SHORT, function string () {
+ object koeder = present(WURM_ID, ME);
+ return Query(P_SHORT,F_VALUE)+
+ (objectp(koeder)?" mit "+koeder->name(WEM):"");
+ }, F_QUERY_METHOD);
+ Set(P_LONG, function string () {
+ object koeder = present(WURM_ID, ME);
+ return Query(P_LONG,F_VALUE) + (objectp(koeder)?
+ koeder->Name(WER)+" haengt daran.\n":QueryProp(P_LONG_EMPTY));
+ }, F_QUERY_METHOD);
+
+ // P_FISH und P_WATER liefern die Daten eines evtl. vorhandenen Koeders
+ // Wenn kein Koeder dranhaengt, werden P_WATER und P_FISH des Hakens
+ // zurueckgegeben, falls es sich um einen Fake-Koeder handelt wie beim
+ // Spezialhaken; in diesem Fall hat der Haken selbst die Properties
+ // gesetzt.
+ Set(P_FISH, function int () {
+ object koeder = present(WURM_ID, ME);
+ return (objectp(koeder)?koeder->QueryProp(P_FISH):Query(P_FISH));
+ }, F_QUERY_METHOD);
+ Set(P_WATER, function int () {
+ object koeder = present(WURM_ID, ME);
+ return (objectp(koeder)?koeder->QueryProp(P_WATER):Query(P_WATER));
+ }, F_QUERY_METHOD);
+
+ SetProp(P_MATERIAL,([MAT_STEEL:100]));
+
+ AddCmd(({"spiess","spiesse"}),"spiessauf");
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+static int spiessauf(string str) {
+ string haken,wurmname,*substr;
+ object wurm;
+ int amount;
+
+ notify_fail("Was willst Du denn aufspiessen?\n");
+ if (!stringp(str) || !sizeof(str))
+ return 0;
+
+ if( sscanf(str, "%s auf%s", wurmname, haken) != 2 )
+ return 0;
+
+ haken = trim(haken);
+
+ notify_fail("So etwas hast Du nicht bei Dir.\n");
+ if ( !objectp(wurm=present(wurmname,TP)) )
+ return 0;
+
+ notify_fail("Das kannst Du nicht aufspiessen.\n");
+ if( !wurm->id(WURM_ID) )
+ return 0;
+
+ notify_fail("Worauf willst Du "+wurm->name(WEN,1)+" denn spiessen?\n");
+ if ( haken!="" && !id(haken) )
+ return 0;
+
+ notify_fail(break_string("Dazu solltest Du "+name(WEN,1)+" erst einmal "
+ "bei Dir haben.",78));
+ if ( environment(ME) != TP )
+ return 0;
+
+ notify_fail("An dem Haken haengt schon ein Koeder.\n");
+ if ( QueryKoeder() )
+ return 0;
+
+ // Haken und Koeder sind im Inventar und fuehlen sich von der Syntax
+ // angesprochen.
+ if( wurm->IsUnit() ) {
+ // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+ // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+ // Bei der ersten Abfrage wuerde also das hier gesetzte U_REQ wieder
+ // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+ // werden...
+ wurm->QueryProp(U_REQ);
+ wurm->SetProp(U_REQ,1);
+ }
+ tell_object(TP, break_string(
+ "Du spiesst "+wurm->name(WEN,1)+" auf "+name(WEN,1)+".",78));
+ tell_room(environment(TP), break_string(
+ TP->Name(WER)+" spiesst "+wurm->name(WEN)+" auf "+name(WEN)+".",78),
+ ({TP}));
+ // M_GET, damit auch P_NODROP-Koeder aufgespiesst werden koennen.
+ if ( wurm->move(ME,M_GET) != MOVE_OK) {
+ tell_object(TP, break_string(
+ "Verdammt! Jetzt hast Du Dir fast in den Finger gestochen! "+
+ wurm->Name(WEN,1)+" hast Du dabei natuerlich verfehlt.", 78));
+ }
+ return 1;
+}
+
+int QueryKoeder(){
+ return objectp(present(WURM_ID, ME));
+}
+
+object QueryKoederObject() {
+ return present(WURM_ID,ME);
+}
+
+int MayAddObject(object ob) {
+ return (objectp(ob) && ob->id(WURM_ID) && sizeof(all_inventory(ME))<=1);
+}
+
+int _query_total_weight() {
+ int tw;
+ foreach(int w: all_inventory(ME)+({ME}))
+ tw += w->QueryProp(P_WEIGHT);
+ return tw;
+}
+
+varargs int remove(int sil) {
+ all_inventory(ME)->remove(); // funktioniert auch mit leeren Arrays
+ return ::remove(sil);
+}
+
+int KoederGefressen() {
+ // Nicht QueryKoeder() pruefen, da das bei Spezialhaken immer 1 ist.
+ object koeder = present(WURM_ID, ME);
+ if ( objectp(koeder) )
+ return koeder->remove();
+ return -1;
+}
diff --git a/std/items/fishing/koeder.c b/std/items/fishing/koeder.c
new file mode 100644
index 0000000..c63317b
--- /dev/null
+++ b/std/items/fishing/koeder.c
@@ -0,0 +1,28 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <items/fishing/fishing.h>
+
+protected void create(){
+ ::create();
+ AddId(({WURM_ID,"koeder", "wurm","regenwurm"}));
+ SetProp(P_NAME, "Wurm");
+ SetProp(P_GENDER , MALE);
+ SetProp(P_ARTICLE, 1);
+ SetProp(P_FISH, 0);
+ SetProp(P_WATER, 0);
+ SetProp(P_SHORT, "Ein kleiner Wurm");
+ SetProp(P_LONG, "Ein kleiner Regenwurm.\n");
+ SetProp(P_MATERIAL, MAT_MISC_LIVING);
+ SetProp(P_VALUE, 1);
+ SetProp(P_WEIGHT, 5);
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+