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