blob: 7f403f3cd13f3b78bc1d6e39155fb2183f8a97c0 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001/*
2 Zusammenfassung Bedingungen:
3 - Angel auswerfen
4 - Haken muss dran sein
5 - Haken muss Koeder haben
6 - darf nur ein Haken drin sein, sonst nix, und genau ein Haken
7 - Angel im Inventar
8 - Angel darf nicht schon ausgeworfen sein
9 - passendes Gewaesser im Raum
10 - Move-Hook muss korrekt registrierbar sein
11 - Angel einholen
12 - Angel-Callout muss ueberhaupt erstmal laufen
13 - Waehrend des Angelns
14 - bewegen beendet das Angeln
15 - Ausloggen beendet das Angeln
16 - Ende beendet das Angeln
17 - nichts rausnehmen
18 - nichts reintun, ausser Fisch am Ende der Wartezeit
19 - Langbeschreibung zeigt "ausgeworfen", Koeder und gefangenen Fisch an
20 - Fisch fangen (call_out zuende)
21 - Gewaesser passt (immer noch)
22 - Fisch passt noch in die Angel rein
23 - Haken dranhaengen
24 - Haken im Inv
25 - Haken muss "echter" Angelhaken sein
26 - Angel im Inv
27 - Koeder am Haken
28 - noch kein anderer Koeder dran
29 - Angel ist nicht ausgeworfen
30 - generell
31 - nur Haken dranstecken, nix anderes reintun
32 - reintun nur mit Syntax "haenge" etc. (damit sind Fische aussen vor,
33 weil die Syntax auf Haken prueft)
34 (ggf. ist dann das PreventInsert() zu vereinfachen)
35 */
36
37#pragma strong_types, save_types, rtt_checks
38#pragma no_clone, no_shadow
39
40inherit "/std/container";
41inherit "/std/hook_consumer";
42
43#include <new_skills.h>
44#include <language.h>
45#include <properties.h>
46#include <moving.h>
47#include <hook.h>
48#include <wizlevels.h>
49#include <items/fishing/fishing.h>
50#include <items/fishing/aquarium.h>
51
52#define TP this_player()
53#define ME this_object()
54#define BS(x) break_string(x, 78)
55
56#define MIN_DELAY 80 // Mindestdelay bis Fisch beisst!
57#define STD_DELAY_PLUS 60 // +/- 60 sekunden Tolerant
58#define MAX_FISH_BONUS 60 // max. Wartezeitverkuerzung durch P_FISH
59#define WRONGWATER 60 // 1-2 min, falls Angel nicht geeignet
60#define WRONGWORM 60 // 1-2 min, falls Koeder nicht geeignet
61
62#define KOEDER_LOST 10
63// Verlustwahrsch. fuer Koeder falls Fisch zu schwer
64
65private object room, current_user;
66private int active, actime;
67private mapping aquarium = STDFISHLIST;
68
69nomask varargs void StopFishing(string msg_me, string msg_room);
70int IsFishing();
71
72protected void create() {
73 ::create();
74
75 SetProp(P_SHORT, "Eine Angel");
76 SetProp(P_LONG, "Eine nagelneue Angel.\n");
77 SetProp(P_NAME, "Angel");
78 AddId(({"angel", "rute", ANGEL_ID}));
79
80 SetProp(P_GENDER, FEMALE);
81 SetProp(P_MATERIAL,([MAT_MISC_WOOD:90,MAT_WOOL:10]));
82 SetProp(P_TRANSPARENT,1);
83 SetProp(P_WEIGHT,200);
84 SetProp(P_MAX_WEIGHT, 10000);
85 SetProp(P_WATER, W_USER); //muss (mehrere) Flags gesetzt haben!
86 SetProp(P_SOURCE_PREPOSITION, "von");
87 SetProp(P_NR_HANDS, 2);
88
89 Set(P_NODROP, function string () {
90 return (IsFishing()?"Du solltest die Angel nicht fallenlassen, solange "
91 "Du angelst.\n":0);
92 }, F_QUERY_METHOD);
93
94 AddCmd(({"angel","angle"}),"angel");
95 AddCmd("hole|hol&@ID&ein", "stopit", "Was willst Du (ein)holen?|"
96 "Willst Du @WEN2 einholen?");
97 AddCmd("haenge|haeng|befestige&@PRESENT&@ID", "move_in",
98 "Was willst Du woran @verben?|"
99 "Woran willst Du @WEN2 denn @verben?");
100
MG Mud User88f12472016-06-24 23:31:02 +0200101 AddCmd("angeltest","qangel");
102}
103
104protected void create_super() {
105 set_next_reset(-1);
106}
107
108static int stopit(string str) {
109 if( find_call_out("do_angel")==-1 )
110 tell_object(TP, "Du angelst nicht!\nTP, ");
111 else
112 StopFishing();
113 return 1;
114}
115
116varargs string long(int mode) {
117 string descr = QueryProp(P_LONG);
118 if(!QueryProp(P_TRANSPARENT))
119 return descr;
120
121 if ( find_call_out("do_angel")!=-1 )
122 descr += capitalize(QueryPronoun(WER)) + " ist ausgeworfen.\n";
123
124 object *inv = all_inventory(ME);
125 string inv_descr = make_invlist(TP, inv);
126 if ( inv_descr != "" )
127 descr += "An "+QueryPronoun(WEM) + " haeng"+(sizeof(inv)>1?"en":"t")+
128 ":\n" + inv_descr;
129
130 return descr;
131}
132
133/*int tell_stat()
134{
135 int n;
136
137 if(!IS_WIZARD(TP))
138 return 0;
139 if(!active){
140 write("Du angelst nicht\n"); return 1;
141 }
142 n=find_call_out("do_angel");
143 if(active)
144 write(
145 "----------------------------------------------------------------------\n"
146 +"Der Fisch beisst in "+n+" sec!\n"
147 +"Bestandswert des Raumes: "+room->QueryProp(P_FISH)
148 +" = "+(room->QueryProp(P_FISH)+100)+"%.\n"
149 +"Bestandswert des Koeders: "+koeder->QueryProp(P_FISH)
150 +" = "+(100+koeder->QueryProp(P_FISH))+"%.\n"
151 +"Gesamt:"+bestand+" = "+(bestand+100)+"%.\n");
152 if(!(QueryProp(P_WATER)&room->QueryProp(P_WATER)))
153 write("Die Angel passt nicht zum Gewaessertypen.\n");
154 else
155 write("Die Angel passt zum Gewaessertypen.\n");
156 if(!(room->QueryProp(P_WATER)&koeder->QueryProp(P_WATER)))
157 write("Der Koeder passt nicht zum Gewaessertypen.\n");
158 else
159 write("Der Koeder passt zum Gewaessertypen.\n");
160 write(" => Delay="+delay+".\n"
161 +"----------------------------------------------------------------------\n"
162);
163 return 1;
164}*/
165
166static int move_in(string str, mixed *param) {
167 object haken = param[0];
168 // param[1] sind wir selbst, daher ab hier ME verwendet.
169
170 if ( find_call_out("do_angel")!=-1 ) { // angelt noch nicht?
171 tell_object(TP, BS(Name(WER,1)+" ist bereits ausgeworfen, da haengst "
172 "Du jetzt besser nichts mehr dran, das vertreibt nur die Fische."));
173 }
174 else if ( !haken->id(HAKEN_ID) ) { // echter Angelhaken?
175 tell_object(TP, BS(haken->Name(WER,1)+" ist kein Angelhaken, "+
176 haken->QueryArticle(WEN, 1, 1)+"kannst Du nicht an "+name(WEN,1)+
177 " haengen."));
178 }
179 else if ( environment(haken) != TP ) { // Haken im Inv?
180 tell_object(TP, BS("Wie er da so auf dem Boden liegt, schaffst Du es "
181 "einfach nicht, "+haken->name(WEN,1)+" vernuenftig an "+name(WEM,1)+
182 " zu befestigen."));
183 }
184 else if ( environment(ME) != TP ) { // Angel im Inv?
185 tell_object(TP, BS("Es stellt sich viel fummeliger heraus als gedacht, "+
186 haken->name(WEN,1)+" an "+name(WEM,1)+" zu befestigen, solange "+
187 QueryPronoun(WER)+" auf dem Boden herumliegt."));
188 }
189 else if ( present(HAKEN_ID, ME) ) { // anderer Haken schon dran?
190 tell_object(TP, BS("An "+name(WEM,1)+" haengt bereits ein Angelhaken."));
191 }
192 else if ( !haken->QueryKoeder() ) { // Koeder am Haken?
193 tell_object(TP, BS("Du willst "+haken->name(WEN,1)+" gerade an "+
194 name(WEN,1)+" haengen, als Dir auffaellt, dass Du beinahe den Koeder "
195 "vergessen haettest."));
196 }
197 else { // dann darf der Haken rangehaengt werden.
198 if ( haken->move(ME, M_PUT) == MOVE_OK ) {
199 tell_object(TP, BS("Du haengst "+haken->name(WEN,1)+" an "+
200 name(WEN,1)+"."));
201 tell_room(environment(TP), BS(TP->Name(WER)+" haengt etwas metallisch "
202 "Glaenzendes an "+name(WEN)+", vermutlich einen Angelhaken."),
203 ({TP}));
204 }
205 else {
206 tell_object(TP, BS(haken->Name(WER,1)+" laesst sich nicht an "+
207 name(WEM,1)+" befestigen."));
208 }
209 }
210 return 1;
211}
212
213static int qangel() {
214 if(IS_WIZARD(TP)) {
215 call_out("do_angel",1);
216 return 1;
217 }
218 return 0;
219}
220
221static int angel(string str) {
222 object haken = present(HAKEN_ID,ME);
223 if ( environment(ME) != TP ) {
224 tell_object(TP, BS("Dafuer musst Du die Angel in die Hand nehmen."));
225 }
226 else if ( find_call_out("do_angel")!=-1 ) {
227 tell_object(TP, "Das tust Du doch schon.\n");
228 }
229 else if(!objectp(haken)) {
230 tell_object(TP, "Wie soll das gehen? Du hast ja nichtmal einen Haken "
231 "an der Angel!\n");
232 }
233 else if(!haken->QueryKoeder()) {
234 tell_object(TP, break_string("Ohne Koeder am Haken wird sich kein "
235 "Fisch der Welt fuer Deine Angel interessieren.",78));
236 }
237 else if(present(FISCH_ID,ME)) {
238 tell_object(TP, "Nimm erst mal die Beute von der Angel, die noch daran "
239 "zappelt!\n");
240 }
241 else if ( !TP->UseHands(ME, QueryProp(P_NR_HANDS)) ) { // freie Haende?
242 tell_object(TP, BS("Du musst schon beide Haende frei haben, um die "
243 "Angel sicher festhalten zu koennen."));
244 }
245 else {
246 // Raum festhalten, in dem geangelt wird.
247 room = environment(TP);
248 // Gewaessertyp abfragen
249 active = room->QueryProp(P_WATER);
250 // Kein Wasser vorhanden, oder nicht-befischbarer Sondertyp?
251 if( (!active) || (active & W_OTHER) ) {
252 tell_object(TP, "Du kannst hier nicht angeln.\n");
253 TP->FreeHands(ME);
254 }
255 // totes Gewaesser erwischt
256 else if ( active & W_DEAD ) {
257 write("In dem Gewaesser hier gibt es keine Fische.\n");
258 TP->FreeHands(ME);
259 }
260 // Jetzt aber: es gibt Fisch, Baby. ;-)
261 else {
262 int delay = MIN_DELAY; // 80 Sekunden.
263 // Fischbestand ermitteln aus Raum- und Koederparametern
264 int bonus = room->QueryProp(P_FISH) + haken->QueryProp(P_FISH);
265 // Ist kein Bonus, sondern ein Malus rausgekommen?
266 if ( bonus < 0 ) {
267 // Dann begrenzen auf 5 Min.
268 if ( bonus < -300 )
269 bonus = -300;
270 // Wartezeit erhoehen.
271 delay += random(-bonus);
272 }
273 else {
274 // Bonus deckeln auf Max-Wert (60 Sekunden)
275 if ( bonus > MAX_FISH_BONUS ) {
276 bonus = MAX_FISH_BONUS;
277 }
278 // Delay reduzieren (minimal 20 Sekunden)
279 delay -= random(bonus);
280 }
281
282 // passt das Gewaesser zur Angel/zum Koeder ?
283 if( !(QueryProp(P_WATER) & room->QueryProp(P_WATER)) )
284 delay += WRONGWATER + random(WRONGWATER);
285 if( !(room->QueryProp(P_WATER) & haken->QueryProp(P_WATER)) &&
286 room->QueryProp(P_WATER) != W_USER )
287 delay += WRONGWORM + random(WRONGWORM);
288
289 int hook = TP->HRegisterToHook(H_HOOK_MOVE, ME, H_HOOK_OTHERPRIO(1),
290 H_LISTENER, 0);
291 if ( hook != 1 ) {
292 active = 0;
293 room = 0;
294 TP->FreeHands(ME);
295 tell_object(TP, BS(
296 "Du schleuderst die Angel mit Schwung in Richtung Wasser, aber der "
297 "Haken verfaengt sich in Deinen Sachen und piekst Dir in den "
298 "Allerwertesten. Das versuchst Du besser noch einmal anders."));
299 tell_room(environment(TP), BS(
300 TP->Name()+" schleudert eine Angel in Richtung Wasser, ist dabei "
301 "aber so ungeschickt, dass sich der Haken verfaengt."), ({TP}));
302 }
303 else {
304 // Alle Bedingungen erfuellt:
305 // - Angel in der Hand.
306 // - Gewaesser stimmt.
307 // - Haken mit Koeder ist dran.
308 // - alte Beute wurde abgenommen.
309 // - Move-Hook gesetzt, um Abbruch ausloesen zu koennen.
310 actime = time();
311 current_user = TP;
312 tell_object(TP, "Du wirfst die Angel aus.\n");
313 tell_room(environment(TP), TP->Name()+" wirft eine Angel aus.\n",
314 ({TP}));
315 call_out("do_angel", delay);
316 }
317 }
318 }
319 return 1;
320}
321
322private object GetFish(object room) {
323 int wtype = room->QueryProp(P_WATER);
324 string *fische;
325
326 // Wenn GetAquarium() bei W_USER nichts zurueckliefert, geht der Angler
327 // leer aus. Es kann kein Fisch aus der Std-Liste genommen werden, weil
328 // der Gewaessertyp ja per Definition benutzerdefiniert ist.
329 if ( wtype == W_USER )
330 fische = room->GetAquarium(ME)||({});
331 else
332 fische = aquarium[wtype];
333
334 string beute = fische[random(sizeof(fische))];
335
336 if ( !beute )
337 return 0;
338 // GetAquarium() liefert volle Pfade, STDFISHLIST nur Dateinamen relativ
339 // zu FISH()
340 else if ( wtype == W_USER )
341 return clone_object(beute);
342 else
343 return clone_object(FISH(beute));
344}
345
346static void do_angel() {
347 object haken = present(HAKEN_ID, ME);
348 object room = environment(TP);
349 object fisch;
350
351 string msg_self, msg_other;
352 if ( member(TP->QueryProp(P_HANDS_USED_BY), ME)==-1 ) {
353 tell_object(TP, BS("Waehrend Du vertraeumt vor Dich hingeangelt hast, "
354 "hat sich Dein Griff an der Angel wohl gelockert, so dass der "
355 "Fisch, der gerade anbeissen wollte, sie Dir beinahe entrissen "
356 "haette."));
357 tell_room(environment(TP), BS(TP->Name(WER)+" hat sich gerade "
358 "ziemlich erschreckt, als "+TP->QueryPronoun(WEM)+" beinahe "+
359 TP->QPP(ME, WEN, SINGULAR)+" "+Name(RAW)+" entrissen worden waere."),
360 ({TP}));
361 if ( !random(3) && haken->KoederGefressen() )
362 tell_object(TP, BS("Der Koeder ist jedenfalls futsch."));
363 return;
364 }
365
366 if ( !objectp(haken) ) {
367 msg_self = "Huch, anscheinend hat sich in einem unbeobachteten "
368 "Moment der Angelhaken von der Angelschnur geloest. War der Knoten "
369 "doch nicht gut genug gesichert?";
370 msg_other = TP->Name()+" zieht unglaeubig eine leere Angelschnur aus "
371 "dem Wasser.";
372 }
373 else if ( !objectp(fisch=GetFish(room)) ||
374 active != room->QueryProp(P_WATER) )
375 {
376 msg_self = "Anscheinend gibt es hier doch keine Fische. Du gibst "
377 "auf und holst "+name(WEN,1)+" wieder ein.";
378 // Leaken von Fischobjekten verhindern.
379 if (objectp(fisch))
380 fisch->remove();
381 }
382 else if ( fisch->move(ME) != MOVE_OK ) {
383 msg_self = fisch->Name(WER)+" biss an, war aber zu schwer fuer "
384 "Dich (oder Deine Angel). "+capitalize(fisch->QueryPronoun(WER))+
385 " entgleitet Dir und plumpst zurueck ins Wasser!";
386
387 if( !(fisch->QueryProp(P_FISH) & F_NOTHUNGRY) ) {
388 haken->KoederGefressen();
389 if(!random(KOEDER_LOST)) {
390 msg_self += " Dein Haken ist dabei leider abgerissen!";
391 tell_room(environment(TP), BS(Name(WER,1)+" von "+TP->Name(WEM)+
392 " wackelte sehr stark."), ({TP}));
393 haken->remove();
394 }
395 }
396 fisch->remove(1);
397 }
398 else {
399 haken->KoederGefressen();
400 }
401 StopFishing(msg_self, msg_other);
402}
403
404varargs int remove(int silent) {
405 if ( find_call_out("do_angel")!=-1 )
406 StopFishing(Name(WER,1)+" loest sich ploetzlich im Nichts auf.");
407 return ::remove(silent);
408}
409
410// Jemand macht "ende" waehrend er angelt?
411protected void NotifyMove(object dest, object oldenv, int method) {
412 if ( find_call_out("do_angel")!=-1 && oldenv == current_user &&
413 dest->IsRoom() ) {
414 StopFishing("Du holst die Angel ein und legst sie beiseite.");
415 }
416 return ::NotifyMove(dest, oldenv, method);
417}
418
419// Spieler loggt waehrend des Angelns aus? Dann angeln einstellen.
420void BecomesNetDead(object pl) {
421 if ( find_call_out("do_angel")!=-1 && pl == current_user ) {
422 StopFishing("Angeln ist so eine beruhigende Freizeitbeschaeftigung! "
423 "Bevor Du mit der Angel in der Hand einschlaeft, holst Du sie "
424 "lieber schnell ein.", pl->Name()+" holt schnell noch die Angel ein, "
425 "bevor "+pl->QueryPronoun(WER)+" einschlaeft.");
426 }
427}
428
429int PreventInsert(object ob) {
430 // Funktion wird aus einer Spielereingabe heraus gerufen
431 // Ich nehme hier nicht TP, weil der call_out("do_angel") die
432 // gefangenen Fische per move() in die Angel bewegt: Bei call_out()s wird
433 // TP durch die Kette weitergereicht, so dass dann hier keine
434 // Unterscheidung zwischen Spielereingabe und call_out() moeglich waere.
435 SetProp(P_NOINSERT_MSG, 0);
436 if ( stringp(query_verb()) &&
437 member(({"haenge","haeng","befestige"}), query_verb()) == -1 ) {
438 SetProp(P_NOINSERT_MSG, BS(Name(WER,1)+" ist nur dafuer geeignet, "
439 "Angelhaken daranzuhaengen."));
440 return 1;
441 }
442 if( ob->id(FISCH_ID) ) {
443 write("Etwas zappelt an Deiner Angel.\n");
444 }
445 return ::PreventInsert(ob);
446}
447
448// Wenn geangelt wird, nimmt hier niemand was raus.
449public int PreventLeave(object ob, mixed dest) {
450 SetProp(P_NOLEAVE_MSG, 0);
451 if ( find_call_out("do_angel")!=-1 ) {
452 if ( objectp(TP) && ob->id(HAKEN_ID) )
453 SetProp(P_NOLEAVE_MSG, BS("Der Haken ist gerade unter Wasser. Es "
454 "waere kontraproduktiv, ihn ausgerechnet jetzt abzunehmen."));
455 return 1;
456 }
457 return ::PreventLeave(ob, dest);
458}
459
460// Beendet das Angeln, indem Aktiv-Flag geloescht, Hook deregistriert
461// und call_out() geloescht wird. Zusaetzlich werden die als Argument ueber-
462// gebenen Meldungen ausgegeben, oder passende generische verwendet.
463nomask varargs void StopFishing(string msg_me, string msg_room) {
464 active = 0;
465 while(remove_call_out("do_angel")!=-1);
466 object env = environment(ME);
467 if ( objectp(env) && env == current_user ) {
468 env->FreeHands(ME);
469 current_user = 0;
470 env->HUnregisterFromHook(H_HOOK_MOVE, ME);
471 tell_object(env, BS(msg_me||"Du holst Deine Angel wieder ein."));
472 tell_room(environment(env), BS(msg_room||
473 env->Name(WER)+" holt "+env->QueryPossPronoun(ME,WEN,SINGULAR)+" "+
474 name(RAW)+" ein."), ({env}));
475 }
476}
477
478// Diese Methode wird in jedem Hook-Konsumenten eines Hook-Providers
479// aufgerufen, solange die Verarbeitung nicht vorher abgebrochen wurde.
480// Dann jedoch wurde auch die Aktion (Bewegung) nicht ausgefuehrt, d.h.
481// solange hier kein Event ankommt, wurde keine Bewegung ausgefuehrt und
482// das Angeln kann weiterlaufen.
483mixed HookCallback(object hookSource, int hookid, mixed hookData) {
484 if ( hookid == H_HOOK_MOVE && hookSource == current_user ) {
485 StopFishing("Deine Angelschnur ist zu kurz, um damit rumlaufen zu "
486 "koennen. Du holst die Angel wieder ein.");
487 }
488 return ({H_NO_MOD, hookData});
489}
490
491// Wird gerufen, wenn der Konsument von einem anderen mit hoeherer Prioritaet
492// verdraengt wurde.
493void superseededHook(int hookid, object hookSource) {
494 if ( hookid == H_HOOK_MOVE && hookSource == current_user ) {
495 StopFishing("Irgendetwas ist gerade passiert, das alle Fische "
496 "vertrieben hat. Du siehst sie unter der Wasseroberflaeche davon"
497 "flitzen. Ernuechtert holst Du Deine Angel wieder ein.");
498 }
499}
500
501// Angelzustand abfragen.
502int IsFishing() {
503 return (find_call_out("do_angel")!=-1);
504}
505
506// Gewaessertyp abfragen, in dem gerade geangelt wird.
507int query_active() {
508 return active;
509}