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