blob: 1555b4e5213a23086bdd836b4a1aec825ab67480 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// clothing/wear.c -- Funktionen rund ums Anziehen/Tragen von Kleidung.
4//
5// $Id: combat.c 6243 2007-03-15 21:10:21Z Zesstra $
6
7#pragma strict_types
8#pragma save_types
9#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020010#pragma range_check
11
12#define NEED_PROTOTYPES
13
14#include <thing/properties.h>
15#include <thing/commands.h>
16#include <thing/description.h>
17#include <living/clothing.h>
18#include <clothing.h>
19#include <living/combat.h>
20#include <language.h>
21#include <defines.h>
22#include <new_skills.h>
23#include <moving.h>
24
25// Globale Variablen
26nosave int flaw, ftime;
27
28void create()
29{
30 // Einige Properties sollten nicht von aussen gesetzt werden koennen
31 Set(P_WORN, PROTECTED, F_MODE);
32 Set(P_LAST_USE, PROTECTED, F_MODE_AS);
33 Set(P_RESTRICTIONS,([]),F_VALUE);
34
35 // Bekleidung benoetigt Kommandos, mit denen man sie an- und
36 // ausziehen kann
37 AddCmd( ({"zieh","ziehe"}),"ziehe" );
38 AddCmd( ({"trag","trage"}),"do_wear" );
39}
40
41// aktuelles Lebewesen, was diese Kleidung (oder auch Ruestung) zur Zeit
42// traegt.
43public object QueryUser()
44{
45 return QueryProp(P_WORN);
46}
47
48
49// Ausgabe von Meldungen ueber write() oder _notify_fail(), je nachdem, ob der
50// Spieler alles anzieht oder was bestimmtes.
51protected void msg(string str, mixed fl) {
52 if (!stringp(str)) {
53 return;
54 }
55 if (fl) {
56 write(str);
57 }
58 else {
59 _notify_fail(str);
60 }
61}
62
63/*
64 * Ausgabe einer Meldung beim Anziehen geht nur an Spieler, nicht an NPC.
65 * Die Umgebung bekommt immer eine Meldung.
66 */
67varargs void doWearMessage(int all) {
68 string *str,s1;
69 mixed wearmsg;
70
71 if(wearmsg=QueryProp(P_WEAR_MSG)) { // Ist eine WearMsg gesetzt?
72 if(closurep(wearmsg)) { // Evtl. gar als extra Fkt.?
73
74 str = funcall(wearmsg, PL);
75 if(interactive(PL)) {
76 // Im Falle, dass all gesetzt ist, wird als Indent der Name des
77 // angezogenen Objektes gesetzt. (trag alles)
78 write(break_string(str[0],78,
79 (all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
80 }
81 //(Zesstra): say() ist hier bloed, weil es an das Env vom this_player()
82 //ausgibt, sofern der existiert. So koennen Spieler die meldung kriegen,
83 //obwohl die laengst woanders sind (z.B. Sequenzen)
84 //Daher nun Ausgabe an das Env vom Env (wenn das kein Raum sein sollte,
85 //was durchaus sein koennte, macht tell_room() nix).
86 if ( objectp(environment()) && objectp(environment(environment())) )
87 tell_room(environment(environment()),
88 break_string(str[1], 78, 0, BS_LEAVE_MY_LFS),({PL}) );
89
90 return;
91 }
92 else if(interactive(PL)) {
93 s1 = replace_personal(sprintf(wearmsg[0],"@WEN2"), ({PL,ME}), 1);
94
95 write(break_string(s1,78,(all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
96 }
97
98 s1 = replace_personal(sprintf(wearmsg[1],"@WER1","@WENU2"),
99 ({PL, ME}), 1);
100
101 if ( objectp(environment()) && objectp(environment(environment())) )
102 tell_room(environment(environment()),
103 break_string(s1, 78, 0, BS_LEAVE_MY_LFS),({ PL }) );
104
105 return;
106 }
107 /*
108 * Keine WearMsg gesetzt. Ausgabe der Default-Meldungen.
109 */
110 else if(interactive(PL)) {
111 write(break_string("Du ziehst " + name(WEN,1) + " an.",78,
112 (all?(Name(WER)+": "):0)));
113 }
114 if ( objectp(environment()) && objectp(environment(environment())) )
115 tell_room(environment(environment()),break_string(PL->Name(WER)
116 + " zieht " + name(WEN,0) +" an.",78), ({PL}));
117}
118
119/*
120 * Ausgabe einer Meldung beim Ausziehen geht nur an Spieler, nicht an NPC.
121 * Die Umgebung bekommt natuerlich immer eine Meldung.
122 */
123void doUnwearMessage(object worn_by, int all)
124{
125 string *str,s1;
126 mixed msg;
127
128 if(!objectp(worn_by)) { // Na huch, gar nicht angezogen? Abbruch.
129 return;
130 }
131
132 if(msg=QueryProp(P_UNWEAR_MSG)) { // Ist eine UnwearMsg gesetzt?
133 if(closurep(msg)) { // Oho! Gar gleich als Fkt.?
134
135 str = funcall(msg, worn_by);
136 if(interactive(worn_by)) {
137 tell_object(worn_by,break_string(str[0], 78,
138 (all?(Name(WER)+": "):0),BS_LEAVE_MY_LFS));
139 }
140
141 //(Zesstra): say() ist hier bloed, weil es an das Env vom this_player()
142 //ausgibt, sofern der existiert. So koennen Spieler die meldung kriegen,
143 //obwohl die laengst woanders sind (z.B. Sequenzen)
144 //Daher nun Ausgabe an das Env vom worn_by (wenn das kein Raum sein sollte,
145 //macht tell_room() nix).
146 if ( objectp(environment(worn_by)) )
147 tell_room(environment(worn_by),
148 break_string(str[1],78, 0, BS_LEAVE_MY_LFS),({ worn_by }) );
149
150 return;
151 }
152 else if(interactive(worn_by)) {
153 s1 = replace_personal(sprintf(msg[0],"@WEN2"),
154 ({worn_by,ME}), 1);
155
156 tell_object(worn_by,break_string(s1,78,
157 (all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
158 }
159
160 s1 = replace_personal(sprintf(msg[1],"@WER1","@WENU2"),
161 ({worn_by, ME }), 1);
162
163 if ( objectp(environment(worn_by)) )
164 tell_room(environment(environment()),
165 break_string(s1,78, 0, BS_LEAVE_MY_LFS),({ worn_by }) );
166 return;
167 }
168 /*
169 * Keine UnwearMsg gesetzt. Ausgabe der Default-Meldungen.
170 */
171 else if(interactive(worn_by)) {
172 tell_object(worn_by,break_string("Du ziehst " + name(WEN,1) + " aus.",78,
173 (all?(Name(WER)+": "):0)));
174 }
175 if ( objectp(environment(worn_by)) )
176 tell_room(environment(worn_by), break_string(worn_by->Name(WER)
177 + " zieht " + name(WEN,0) + " aus.",78), ({ worn_by }) );
178}
179
180// Diese Funktion wird aufgerufen, wenn die Ruestung wirklich angezogen
181// wird
182protected void InformWear(object pl, int silent, int all) {
183 return;
184}
185
186// Diese Funktion wird aufgerufen, wenn die Ruestung wirklich ausgezogen
187// wird
188protected void InformUnwear(object pl, int silent, int all) {
189 return;
190}
191
192// liefert Werte <0 zurueck, wenn der Spieler die Kleidung _nicht_
193// anziehen darf.
194// Hierbei steht -1 dafuer, dass der Aufrufer return 0 machen sollte,
195// <= -2 sollte zur einem return !all fuehren.
196protected int _check_wear_restrictions(int silent, int all) {
197 mixed type,res;
198 object *armours;
199
200 // Man kann nur Kram anziehen, die man bei sich traegt
201 if (environment()!=PL) {
202 msg(break_string("Du musst "+name(WEN,1)+" erst nehmen!",78,
203 (all?(Name(WER)+": "):0)), all);
204 return(-1);
205 }
206
207 // Eine getragene Ruestung kann man nicht nochmal anziehen
208 if (QueryProp(P_WORN)) {
209 msg(break_string("Du traegst "+name(WEN,1)+" bereits.",78,
210 (all?(Name(WER)+": "):0)), all);
211 return(-1);
212 }
213
214 // Diese Funktion versucht immer, TP anzuziehen (*args*). Es gibt aber viele
215 // Magier, die ohne TP oder mit dem falschen TP anziehen wollen. Daher mal
216 // pruefen und ggf. Fehler ausloesen.
217 if (!this_player())
218 raise_error("Kein this_player() existent beim Anziehen!\n");
219 else if (this_player() != environment())
220 raise_error("Meine Umgebung beim Anziehen ist nicht this_player()!\n");
221
222 // Ueber P_RESTRICTIONS kann man einige Restriktionen definieren, ohne
223 // gleich auf eine WearFunc zurueckgreifen zu muessen.
224 // Die Auswertung erfolgt ueber den RestrictionChecker
225 if ((res=QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
226 (res=(string)"/std/restriction_checker"->check_restrictions(PL,res))
227 && stringp(res)) {
228 msg(break_string(res,78,(all?(Name(WER)+": "):0)),all);
229 return(-1);
230 }
231
232 // Ist eine WearFunc gesetzt, wird diese aufgerufen.
233 if (objectp(res=QueryProp(P_WEAR_FUNC)) &&
234 !(res->WearFunc(ME, silent, environment()))) {
235 // Eine Meldung muss von der WearFunc ausgegeben werden
236 return(-2);
237 }
238
239 // scheinbar darf man das hier anziehen. ;-)
240 return 0;
241}
242
243protected void _informwear(int silent, int all) {
244
245 // Eine Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
246 if (!silent) {
247 doWearMessage(all);
248 }
249
250 // Inform-Funktion aufrufen
251 InformWear(PL, silent, all);
252}
253
254// Die Funktion, die das eigentliche Anziehen durchfuehrt
255varargs int DoWear(int silent, int all) {
256 int nh;
257
258 // Bedingungen pruefen, _check_restrictions() gibt die notwendigen Meldungen
259 // an den Spieler aus.
260 int res = _check_wear_restrictions(silent, all);
261 if (res == -1)
262 return(0);
263 else if (res <= -2)
264 return(!all);
265
266 // Der Check auf freie Haende muss nach allen anderen Checks aus Kleidung
267 // und Ruestung erfolgen und ist im Prinzip identisch fuer beide. Daher wird
268 // der hier in dieser Funktion gemacht.
269 // Soll das Objekt Haende "benutzen"? Steht da auch wirklich ein
270 // Integer-Wert drin? ich mach da jetzt ein raise_error(), das soll
271 // schliesslich gefixt werden. Ausserdem spart es nen Workaround beim
272 // Ausziehen.
273 if (!intp(nh=QueryProp(P_NR_HANDS))) {
274 raise_error(sprintf("Invalid P_NR_HANDS in %O",object_name()));
275 }
276 // Wenn Haende benutzt werden sollen, muss natuerlich auch getestet
277 // werden, ob das ueberhaupt geht
278 if (nh>0) {
279 if (!(PL->UseHands(ME, nh))) {
280 // Schade, nicht genug Haende frei -> Meldung ausgeben
281 write(break_string("Du hast keine Hand mehr frei.",78,
282 (all?(Name(WER)+": "):0)));
283 return(!all);
284 }
285 }
286
287 // OK, die Ruestung kann angezogen werden.
288 // Behinderung beim Wechsel nur fuer Spieler
289 if (query_once_interactive(PL))
290 // Wenn das Ganze ,,wirklich'' eine Kleidung/Ruestung ist und kein SMS
291 // oder aehnliches...
292 if (!QueryProp(P_WEAPON_TYPE)) {
293 // Aktion noch setzen, Spieler hat ja was angezogen
294 PL->SetProp(P_LAST_WEAR_ACTION,({WA_WEAR,time()}));
295 // Im Kampf verliert der Spieler durch Kleidungswechsel eine Runde.
296 if (PL->InFight()) {
297 PL->SetProp(P_ATTACK_BUSY,1);
298 }
299 }
300 // Eintragen in P_CLOTHING/P_ARMOURS
301 PL->Wear(this_object());
302
303 PL->SetProp(P_EQUIP_TIME,time());
304 SetProp(P_WORN, PL);
305 SetProp(P_EQUIP_TIME,time());
306
307 // ggf. andere Objekte informieren etc.
308 _informwear(silent, all);
309
310 // Fertig mit dem Anziehen. Vorgang beenden bzw. mit anderen
311 // Ruestungen fortfahren
312 return !all;
313}
314
315
316// liefert 0 zureck, wenn die Kleidung/Ruestung ausgezogen werden kann
317// bei M_NOCHECK ist das Ausziehen immer erlaubt, allerdings wird
318// P_REMOVE_FUNC auch dann gerufen (wenn auch ignoriert).
319// <0 verbietet das Ausziehen
320protected int _check_unwear_restrictions(object worn_by, int silent,
321 int all)
322{
323 // Nicht getragene Ruestungen kann man auch nicht ausziehen
324 if (!objectp(worn_by)) {
325 return(-2);
326 }
327
328 // Ist eine RemoveFunc gesetzt, wird diese aufgerufen
329 // Im Falle von M_NOCHECK wird das Ergebnis allerdings ignoriert.
330 mixed res=QueryProp(P_REMOVE_FUNC);
331 if (objectp(res)
332 && !res->RemoveFunc(ME,silent,worn_by)
333 && !(silent & M_NOCHECK)
334 )
335 {
336 // Eine Meldung muss von der RemoveFunc ausgegeben werden
337 return(-2);
338 }
339
340 // generell hebt M_NOCHECK die Restriktionen auf - sonst kommt es zu
341 // massiven Inkonsistenzen beim Bewegen mit M_NOCHECK.
342 if (silent & M_NOCHECK)
343 return 1;
344
345 // Eine verfluchte Ruestung kann man natuerlich nicht ausziehen
346 res=QueryProp(P_CURSED);
347 if (res ) {
348 if (stringp(res)) {
349 // Stand in P_CURSED ein String? Dann diesen ausgeben
350 tell_object(worn_by,
351 (res[<1]=='\n' ? res : break_string(res,78,
352 (all?(Name(WER)+": "):0))));
353 }
354 else {
355 // Sonst eine Standard-Meldung ausgeben
356 tell_object(worn_by,break_string(
357 "Du kannst " + name(WEN) + " nicht ausziehen, " + QueryPronoun(WER)
358 + " ist verflucht worden.\n",78,(all?(Name(WER)+": "):0)));
359 }
360 return(-2);
361 }
362
363 // Ausziehen moeglich
364 return(1);
365}
366
367protected void _informunwear(object worn_by, int silent, int all) {
368
369 // Inform-Funktion aufrufen
370 InformUnwear(worn_by, silent, all);
371
372 // Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
373 if (!(silent&M_SILENT)) {
374 doUnwearMessage( worn_by, all );
375 }
376}
377
378// Die Funktion, die das eigentliche Ausziehen durchfuehrt
379// hier steht nur drin, was auf jeden Fall fuer Kleidungen und Ruestungen
380// gleich ist, damit man bei Ruestungen diese Funktion nicht ueberschreiben
381// muss.
382varargs int DoUnwear(int silent, int all) {
383 object worn_by;
384 int nh;
385
386 // Das Flag "silent" wird in der RemoveFunc() etwas anders behandelt
387 // als ueberall anders. Deshalb wird M_SILENT gesetzt, sofern "silent"
388 // _irgendeinen_ Wert ausser M_NOCHECK hatte.
389 if ( silent & ~M_NOCHECK )
390 silent |= M_SILENT;
391
392 // Standard-Notfiyfail setzen.
393 if (all)
394 notify_fail("Alles ausgezogen, was ging.\n");
395
396 // Hat das Objekt Haende "benutzt"? Steht da auch wirklich ein
397 // Integer-Wert drin? Wenn nicht, mach ich nen raise_error(), das duerfte
398 // eigentlich gar nicht passieren. Pruefung mal am Anfang machen, bevor
399 // irgendwas anderes (RemoveFunc()) passiert ist.
400 if (!intp(nh=QueryProp(P_NR_HANDS))) {
401 raise_error(sprintf("Invalid P_NR_HANDS in %O",object_name()));
402 }
403
404 worn_by=QueryProp(P_WORN);
405 // darf ausgezogen werden? Wenn nicht, Ende.
406 int res = _check_unwear_restrictions(worn_by,silent,all);
407 if (res < 0)
408 return(!all);
409
410 // OK, alles klar, die Ruestung wird ausgezogen
411 worn_by->Unwear(ME);
412
413 // Benutzte Haende wieder freigeben
414 if (nh>0) {
415 worn_by->FreeHands(ME);
416 }
417
418 worn_by->SetProp(P_EQUIP_TIME, time());
419 SetProp(P_WORN, 0);
420
421 // Flag noch setzen, Spieler hat ja was ausgezogen
422 // Aber nur wenns auch der Spieler selbst ist.
423 // und wenn das wirklich eine Ruestung und kein SMS o.ae. ist.
424 if (PL && PL==worn_by && !QueryProp(P_WEAPON_TYPE)) {
425 //Behinderung beim Wechsel nur fuer Spieler
426 if (query_once_interactive(PL)) {
427 PL->SetProp(P_LAST_WEAR_ACTION,({WA_UNWEAR,time()}));
428 if (PL->InFight()) {
429 PL->SetProp(P_ATTACK_BUSY,1);
430 }
431 }
432 }
433
434 // ok, nun noch andere Objekte informieren.
435 _informunwear(worn_by,silent,all);
436
437 // Fertig mit dem Anziehen. Vorgang beenden bzw. mit anderen
438 // Ruestungen fortfahren
439 return !all;
440}
441
442protected int _do_wear(string str, int silent, int all) {
443 int *last;
444
445 // Standard-Notfiy-Fail setzen.
446 if (all)
Zesstra1fd31172018-12-18 21:34:49 +0100447 notify_fail("Alles angezogen was ging.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200448
449 // Ist diese Ruestung ueberhaupt gemeint? Bei "trage alles" ist dies
450 // natuerlich immer der Fall
451 if (!str || (!all && !id(str))) {
452 return 0;
453 }
454
455 // Vielleicht darf der Spieler ja gar nix mehr anziehen.
456 if ((object)PL->InFight()) {
457 last=(int*)PL->QueryProp(P_LAST_WEAR_ACTION);
458 if (pointerp(last) && (last[0]==WA_UNWEAR) && ((time()-last[1])<2)) {
459 notify_fail("Du hast doch gerade erst etwas ausgezogen!\n"
460 "So schnell bist Du nicht!\n");
461 return 0;
462 }
463 }
464
465 // Auf zum eigentlichen Anziehen
466 return DoWear(silent, all);
467
468}
469
470// Funktion, die das "trage"/"ziehe * an"-Kommando auswertet
471varargs int do_wear(string str, int silent) {
472 int all;
473
474 // Hat der Spieler "trage alles" eingegeben?
475 all=(str=="alles" || str=="alle kleidung" || str=="alle bekleidung");
476
477 return(_do_wear(str,silent,all));
478}
479
480protected int _do_unwear(string str, int silent, int all) {
481 int * last;
482
483 // Ist diese Ruestung ueberhaupt gemeint? Und hat dieser Spieler sie
484 // auch wirklich an?
485 if (!stringp(str) || (!all && !id(str))) {
486 return 0;
487 }
488
489 if (!QueryProp(P_WORN)) {
490 if (all) {
491 notify_fail("Alles ausgezogen, was ging.\n");
492 return 0;
493 }
494 if (!Query(P_ARTICLE) || QueryProp(P_PLURAL)) {
495 notify_fail( break_string(
496 "Du traegst k"+name(WEN,0)+".",78) );
497 }
498 else {
499 notify_fail( break_string(
500 "Du traegst "+name(WEN,1)+" nicht.",78) );
501 }
502 return 0;
503 }
504
505 // Vielleicht darf der Spieler ja gar nichts mehr ausziehen.
506 if ((object)PL->InFight()) {
507 last=(int*)PL->QueryProp(P_LAST_WEAR_ACTION);
508 if (pointerp(last) && (last[0]==WA_WEAR) && ((time()-last[1])<2)) {
509 notify_fail("Du hast doch gerade erst etwas angezogen!\n"
510 "So schnell bist Du nicht!\n");
511 return 0;
512 }
513 }
514 // Auf zum eigentlichen Ausziehen
515 return DoUnwear(silent, all);
516}
517
518// Die Funktion, die das "ziehe * aus"-Kommando auswertet
519varargs int do_unwear(string str, int silent) {
520 int all;
521
522 all=(str=="alles" || str=="alle kleidung" || str=="alle bekleidung");
523
524 return(_do_unwear(str,silent,all));
525}
526
527// Funktion, die das "ziehe"-Kommando auswertet
528int ziehe(string str) {
529 string ob;
530
531 // Uebergebenes Argument pruefen
532 if (!stringp(str)) {
533 return 0;
534 }
535
536 // Ist ANziehen gemeint?
537 if (sscanf(str, "%s an", ob)==1) {
538 return do_wear(ob );
539 }
540
541 // Oder ist AUSziehen gemeint?
542 if (sscanf(str, "%s aus", ob)==1 ) {
543 return do_unwear(ob);
544 }
545
546 // Ok, es geht wohl weder ums an- noch ums ausziehen
547 return 0;
548}
549
550
551// Beschaedigen des Kleidungsstuecks
552
553// Direktes Beschaedigen der Kleidung durch Setzen der Prop gibts nicht. ;-)
554// Das geht aus Kompatibilitaetgruenden bei Ruestungen, aber nicht mehr bei
555// Kleidung. Punkt.
556static mixed _set_item_damaged(mixed arg) {
557 return(QueryProp(P_DAMAGED));
558}
559
560// Will man eine Kleidung beschaedigen oder reparieren, so macht man das
561// am besten ueber die Funktion Damage(argument). Positive Argumente
562// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
563// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
564int Damage(int new_dam) {
565 return 0;
566}
567
568// Wird die Kleidung einer Belastung ausgesetzt (bei Ruestungen z.B. bei einem
569// Angriff eines Gegners), dann wird TakeFlaw() aufgerufen. Bei Kleidungen
570// koennte man ja per P_SENSITIVE arbeiten oder ein Magier ruft bei Aktionen
571// TakeFlaw() auf.
572varargs void TakeFlaw(mixed dam_types,mapping einfos) {
573 int quality;
574
575 // Ist der Ruestung eine Qualitaet gesetzt worden, so kann es zu einer
576 // allmaehlichen Beschaedigung der Ruestung kommen. Im if() flaw gleich
577 // hochzaehlen.
578 if ((quality=QueryProp(P_QUALITY)) && !((++flaw) % quality)) {
579 Damage(1);
580 }
581
582 // Zeitpunkt des ersten Aufrufes festhalten
583 if (!ftime)
584 ftime=time();
585}
586
587// Die Flaw-Daten koennen natuerlich auch abgerufen werden
588mixed *QueryFlaw() {
589 return ({flaw,ftime,dtime(ftime)});
590}
591
592public status IsClothing() {return 1;}
593