blob: 90027a4a54c89d01b481be1d1da3f574759c085a [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// living/attributes.c -- attributes for living objects
4//
5// $Id: attributes.c 8375 2013-02-12 21:52:58Z Zesstra $
6
7#include <sys_debug.h>
8
9// attribute handling
10//
11// filter_ldfied: str, dex, con, int
12//
13// In den Attributen und Abilites werden Rassenspzifische und erworbene
14// Faehigkeiten (mit autoload-Eigenschaft) abgepseichert.
15//
16// Funktionen:
17// SetAttribute( attr, val ) (Erzeuge und) setze das Attribut auf val.
18// QueryAttribute( attr ) Gebe den Wert des Attributes zurueck.
19//
20// Wenn ein Objekt eine Funktion _filterattr_<name> beitzt, wird beim Setzen
21// des Attributes <name>, der vorgeschlagene Wert uebergeben und der von
22// dieser Funktion zurueckgegebene gesetzt. (fuer ueberpruefungszwecke)
23// Gleiches gilt fuer _filterabil_<name>.
24
25#pragma strict_types
26#pragma save_types
27#pragma range_check
28#pragma no_clone
29#pragma pedantic
30
31#define NEED_PROTOTYPES
32#include <thing/properties.h>
33#include <attributes.h>
34#include <player/gmcp.h>
35#undef NEED_PROTOTYPES
36
37#include <config.h>
38#include <properties.h>
39
40mapping attributes; // Dies sind die mit ZTs veraenderbaren Attribute
41mapping attributes_modifier; // Modifier sollen gespeichert werden
42nosave mixed* attributes_timed_mods; //P_TIMED_ATTR_MOD
43nosave mapping attributes_offsets; // Offsets NICHT speichern!
44nosave mapping used_attributes_offsets; // Zur Beschleunigung der Berechnung
45nosave object* all_modifiers; // objekte mit P_X/M_ATTR_MOD, P_X/M_HEALTH_MOD
46nosave object* invalid_modifiers; // objekte welche das limit ueberschreiten
47nosave int cumulative_mod; // bilanz der P_X/M_ATTR_MOD
48nosave int hp_off;
49nosave int sp_off;
50
51
52nomask public int SetTimedAttrModifier(string key, mapping modifier,
53 int outdated, object dependent, mixed notify) {
54 if(!key || key=="" || !modifier || (outdated>0 && outdated<time()) ||
55 (notify && !stringp(notify) && !objectp(notify)) ) {
56 return TATTR_INVALID_ARGS;
57 }
58
59
60 if(member(attributes_timed_mods[TATTR_ENTRIES],key)) {
61 // change entry
62 attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]=modifier;
63 attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED]=outdated;
64 attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT]=dependent;
65 attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY]=notify;
66 }
67 else {
68 // add entry
69 attributes_timed_mods[TATTR_ENTRIES]+=([key:modifier;outdated;dependent;notify]);
70 }
71
72 // add outdate
73 if(outdated>0 && member(attributes_timed_mods[TATTR_OUTDATE],outdated)==-1)
74 {
75 attributes_timed_mods[TATTR_OUTDATE]+=({outdated});
76 attributes_timed_mods[TATTR_OUTDATE]=
77 sort_array(attributes_timed_mods[TATTR_OUTDATE],#'<);
78 }
79
80 // add dependent
81 if(objectp(dependent))
82 {
83 if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
84 {
85 attributes_timed_mods[TATTR_DEPENDENTS][key]=dependent;
86 }
87 else
88 {
89 attributes_timed_mods[TATTR_DEPENDENTS]+=([key:dependent]);
90 }
91 }
92 else
93 {
94 // remove previously set dependent
95 if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
96 {
97 attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
98 }
99 }
100
101 UpdateAttributes();
102
103 return TATTR_OK;
104}
105
106nomask public mapping QueryTimedAttrModifier(string key)
107{
108 int outdated;
109 object dependent;
110 mixed notify;
111 mapping mod;
112
113 if(!key || key=="")
114 {
115 return ([]);
116 }
117
118 if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
119 {
120 return ([]);
121 }
122
123 mod=deep_copy(attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]);
124 outdated=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED];
125 dependent=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT];
126 notify=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY];
127
128 return ([key:mod;outdated;dependent;notify ]);
129}
130
131nomask public int DeleteTimedAttrModifier(string key)
132{
133 if(!key || key=="")
134 {
135 return TATTR_INVALID_ARGS;
136 }
137
138 if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
139 {
140 return TATTR_NO_SUCH_MODIFIER;
141 }
142
143 attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
144 attributes_timed_mods[TATTR_ENTRIES]-=([key]);
145 UpdateAttributes();
146
147 return TATTR_OK;
148}
149
150nomask protected void attribute_hb()
151{
152 int now,i,k,update,outdated;
153 string* keys;
154 mapping tonotify;
155
156 // initialize
157 now=time();
158 tonotify=([]);
159
160 keys=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
161
162 // delete outdated
163 for(i=sizeof(attributes_timed_mods[TATTR_OUTDATE])-1;i>=0;i--)
164 {
165 outdated=attributes_timed_mods[TATTR_OUTDATE][i];
166 if(outdated>now)
167 {
168 break;
169 }
170
171 for(k=sizeof(keys)-1;k>=0;k--)
172 {
173 if(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_OUTDATED]==outdated)
174 {
175 // bei fehlendem notifier wurde das zum verhaengnis
176 // dank an gloinson
177 /*
178 if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]))
179 {
180 */
181 tonotify+=([keys[k]:attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]]);
182 //}
183
184 attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[k]]);
185 attributes_timed_mods[TATTR_ENTRIES]-=([keys[k]]);
186 keys-=({keys[k]});
187 }
188 }
189
190 attributes_timed_mods[TATTR_OUTDATE]-=({outdated});
191 }
192
193 // delete depending
194 keys=m_indices(attributes_timed_mods[TATTR_DEPENDENTS]);
195 for(i=sizeof(keys)-1;i>=0;i--)
196 {
197 if(!objectp(attributes_timed_mods[TATTR_DEPENDENTS][keys[i]]))
198 {
199 // siehe oben
200 /*
201 if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]))
202 {
203 */
204 tonotify+=([keys[i]:attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]]);
205 //}
206
207 attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[i]]);
208 attributes_timed_mods[TATTR_ENTRIES]-=([keys[i]]);
209 keys-=({keys[i]});
210 }
211 }
212
213
214 // update
215 if(sizeof(tonotify))
216 {
217 UpdateAttributes();
218 call_out(#'notifyExpiredModifiers,0,tonotify);
219 }
220}
221
222nomask protected void notifyExpiredModifiers(mapping nots)
223{
224 int i;
225 string* keys;
226 while(remove_call_out(#'notifyExpiredModifiers)!=-1);
227
228 if(!nots)
229 {
230 return;
231 }
232
233 keys=m_indices(nots);
234 for(i=sizeof(nots)-1;i>=0;i--)
235 {
236 if(nots[keys[i]] &&
237 ( objectp(nots[keys[i]])||stringp(nots[keys[i]]) ) )
238 {
239 call_other(nots[keys[i]],"NotifyTimedAttrModExpired",keys[i]);
240 }
241 }
242}
243
244
245// invalide modifier benachrichtigen
246nomask protected void notifyInvalidModifiers() {
247
248 while(remove_call_out(#'notifyInvalidModifiers)!=-1);
249
250 if(!invalid_modifiers) {
251 invalid_modifiers=({});
252 }
253
254 call_other(invalid_modifiers,"NotifyXMAttrModLimitViolation");
255
256}
257
258
259// welche Modifier / modif. Objekte wirken nun?
260protected nomask void calculate_valid_modifiers() {
261 closure qp;
262 mapping res;
263 string key;
264 int wert;
265 // Unterscheidung Bonus <-> Malus, weil der Malus voll eingehen soll
266 int hp_malus, sp_malus;
267
268 used_attributes_offsets=([]);
269 cumulative_mod=0;
270 hp_off=sp_off=0;
271 invalid_modifiers=({});
272
273 // rassenspezifische boni P_ATTRIBUTES_OFFSETS
274 if ( mappingp(attributes_offsets) )
275 used_attributes_offsets+=attributes_offsets;
276
277 if (!pointerp(all_modifiers) || !sizeof(all_modifiers)) {
278 // in diesem Fall koennen wir hier direkt mit dieser Funktion Schluss
279 // machen. ;-)
280 return;
281 }
282 else
283 all_modifiers-=({0}); //zerstoerte Objekte rauswerfen.
284
285 // einmal ueber alle modifizierenden Objekt iterieren und aufaddieren
286 foreach(object ob: all_modifiers) {
287 qp = symbol_function("QueryProp",ob);
288
289 if (!objectp(ob) || environment(ob)!=this_object()) {
290 all_modifiers-=({ob});
291 continue;
292 }
293
294 // ext. Attribut-Modifier
295 if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
296 foreach(key, wert: res) {
297 cumulative_mod+=wert;
298 }
299 }
300
301 // magic Attribut-Modifier
302 if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
303 && ( funcall(qp,P_WORN)
304 || funcall(qp,P_WIELDED) ) ) {
305 foreach(key, wert: res) {
306 cumulative_mod+=wert;
307 }
308 }
309 // Magic Health-Modifier
310 if ( mappingp(res=funcall(qp,P_M_HEALTH_MOD))
311 && ( funcall(qp,P_WORN)
312 || funcall(qp,P_WIELDED) ) ) {
313 if (res[P_HP] < 0)
314 hp_malus += res[P_HP];
315 else
316 hp_off+=res[P_HP];
317
318 if (res[P_SP] < 0)
319 sp_malus += res[P_SP];
320 else
321 sp_off+=res[P_SP];
322 }
323
324 // external Health Modifier
325 if ( mappingp(res=funcall(qp,P_X_HEALTH_MOD)) ) {
326 if (res[P_HP] < 0)
327 hp_malus += res[P_HP];
328 else
329 hp_off+=res[P_HP];
330
331 if (res[P_SP] < 0)
332 sp_malus += res[P_SP];
333 else
334 sp_off+=res[P_SP];
335 }
336
337 } // Ende 1. foreach()
338
339
340 // und nochmal, soviele Objekt wieder rausschmeissen, bis das Limit wieder
341 // unterschritten wird. (Verbesserungsvorschlaege erwuenscht.) :-(
342 foreach(object ob: all_modifiers) {
343
344 qp = symbol_function("QueryProp",ob);
345
346 if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
347 if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {
348 invalid_modifiers+=({ob});
349 foreach(key, wert: res) {
350 cumulative_mod-=wert;
351 }
352 }
353 else {
354 add_offsets(res);
355 }
356 }
357
358 if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
359 && ( funcall(qp,P_WORN)
360 || funcall(qp,P_WIELDED) ) ) {
361 if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {
362 if(member(invalid_modifiers,ob)==-1) {
363 invalid_modifiers+=({ob});
364 }
365 foreach(key, wert: res) {
366 cumulative_mod-=wert;
367 }
368 }
369 else {
370 add_offsets(res);
371 }
372 }
373 }
374
375 // HEALTH_MOD werden durch eine Formel 'entschaerft', damit man nur schwer
376 // das Maximum von 150 erreichen kann. (Formel von Humni, beschlossen auf 3.
377 // EM-Treffen)
378 // Mali gehen aber voll ein
379 hp_off = (int)(150 - (150*150.0/(150 + hp_off))) + hp_malus;
380 sp_off = (int)(150 - (150*150.0/(150 + sp_off))) + sp_malus;
381
382 /* alte Version
383 hp_off += hp_malus;
384 sp_off += sp_malus;
385 */
386
387 // notify invalid modifiers
388 if(sizeof(invalid_modifiers)>0) {
389 call_out(#'notifyInvalidModifiers,0);
390 }
391}
392
393// abmelden eines modifiers
394nomask public void deregister_modifier(object modifier)
395{
396 if (!pointerp(all_modifiers)) {
397 return;
398 }
399
400 // valid object?
401 if (!objectp(modifier) || member(all_modifiers,modifier)==-1) {
402 return;
403 }
404
405 all_modifiers-=({modifier});
406 if (invalid_modifiers) {
407 invalid_modifiers-=({modifier});
408 }
409}
410
411// anmelden eines modifiers
412nomask public void register_modifier(object modifier) {
413 closure qp;
414
415 if (!pointerp(all_modifiers)) {
416 all_modifiers=({});
417 }
418
419 // valid object?
420 if (!objectp(modifier) || environment(modifier)!=this_object() ||
421 member(all_modifiers,modifier)!=-1) {
422 return;
423 }
424
425 qp = symbol_function("QueryProp",modifier);
426 // modifier after all? Die P_M_* muessen getragen/gezueckt sein.
427 if(mappingp(funcall(qp,P_X_ATTR_MOD)) ||
428 mappingp(funcall(qp,P_X_HEALTH_MOD)) ||
429 ((mappingp(funcall(qp,P_M_ATTR_MOD)) ||
430 mappingp(funcall(qp,P_M_HEALTH_MOD))) &&
431 (funcall(qp,P_WORN) || funcall(qp,P_WIELDED)) ) ) {
432 all_modifiers+=({modifier});
433 }
434}
435
436protected void add_offsets(mapping arr) {
437 mixed *ind;
438 int i;
439
440 if ( !mappingp(arr) )
441 return;
442
443 foreach(string key, int wert: arr) {
444 used_attributes_offsets[key]+=wert;
445 }
446}
447
448public void UpdateAttributes() {
449 mixed *ind;
450 int i;
451
452 // alle gueltigen Modifier ermitteln aus Objekten im Inventar.
453 calculate_valid_modifiers();
454
455 // persistente modifier drauf (frosch zb)
456 // aus P_ATTRIBUTES_MODIFIERS
457 if ( mappingp(attributes_modifier) ) {
458 foreach(mixed key, mapping wert: attributes_modifier) {
459 add_offsets(wert); // Modifier addieren...
460 }
461 }
462 // timed modifier drauf aus P_TIMED_ATTR_MOD
463 ind=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
464 for ( i=sizeof(ind)-1 ; i>=0 ; i-- ) {
465 // Modifier addieren...
466 add_offsets(attributes_timed_mods[TATTR_ENTRIES][ind[i],0]);
467 }
468 // Bei Monstern werden die HP/SP ueblicherweise selbst gesetzt
469 if ( !query_once_interactive(this_object()))
470 return;
471
472 SetProp(P_MAX_HP, QueryAttribute(A_CON)*8+42+hp_off);
473 SetProp(P_MAX_SP, QueryAttribute(A_INT)*8+42+sp_off);
474
475 if(QueryProp(P_HP)>QueryProp(P_MAX_HP)) {
476 SetProp(P_HP,QueryProp(P_MAX_HP));
477 }
478
479 if(QueryProp(P_SP)>QueryProp(P_MAX_SP)) {
480 SetProp(P_SP,QueryProp(P_MAX_SP));
481 }
482
483 GMCP_Char( ([ A_INT: QueryAttribute(A_INT),
484 A_DEX: QueryAttribute(A_DEX),
485 A_STR: QueryAttribute(A_STR),
486 A_CON: QueryAttribute(A_CON) ]) );
487}
488
489
490protected void create() {
491 hp_off=sp_off=0;
492 used_attributes_offsets=([]);
493 all_modifiers=({});
494 invalid_modifiers=({});
495 cumulative_mod=0;
496
497 Set(P_ATTRIBUTES_MODIFIER, attributes_modifier=([])); // nicht geschuetzt
498 Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=([]));
499 Set(P_ATTRIBUTES_OFFSETS, PROTECTED, F_MODE);
500 Set(P_ATTRIBUTES, attributes=([]));
501 Set(P_ATTRIBUTES, PROTECTED, F_MODE);
502
503 Set(P_TIMED_ATTR_MOD, attributes_timed_mods=({ ({}),([]),([]) }));
504 Set(P_TIMED_ATTR_MOD, NOSETMETHOD|SECURED, F_MODE_AS);
505}
506
507static mixed _query_timed_attr_mod() {
508 mixed ret;
509 return Set(P_TIMED_ATTR_MOD,
510 ({attributes_timed_mods[0],
511 deep_copy(attributes_timed_mods[1]),
512 deep_copy(attributes_timed_mods[2])}));
513}
514
515static mapping _set_attributes(mapping arr)
516{
517 Set(P_ATTRIBUTES, attributes=arr);
518 UpdateAttributes();
519 return arr;
520}
521
522static mapping _query_attributes()
523{
524 return deep_copy(Set(P_ATTRIBUTES, attributes));
525}
526
527static mapping _set_attributes_offsets(mapping arr)
528{
529 Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=arr);
530 UpdateAttributes();
531 return attributes_offsets;
532}
533
534static mapping _query_attributes_offsets()
535{
536 return deep_copy(Set(P_ATTRIBUTES_OFFSETS, attributes_offsets));
537}
538
539static mixed _set_attributes_modifier(mixed arr)
540{ string fn;
541 mixed pre;
542 mapping map_ldfied;
543
544 if ( pointerp(arr) && (sizeof(arr)>=2) )
545 {
546 pre=arr[0];
547 map_ldfied=arr[1];
548 }
549 else
550 {
551 pre=previous_object();
552 map_ldfied=arr;
553 }
554
555 if ( objectp(pre) )
556 fn=old_explode(object_name(pre),"#")[0];
557 else
558 fn=pre;
559
560 if ( !stringp(fn) )
561 return 0;
562
563 // wenn Modifier kein mapping oder ein leeres Mapping: loeschen
564 if ( !mappingp(map_ldfied) || !sizeof(map_ldfied))
565 m_delete(attributes_modifier,fn);
566 else
567 attributes_modifier[fn]=map_ldfied;
568
569 Set(P_ATTRIBUTES_MODIFIER, attributes_modifier);
570 UpdateAttributes();
571 return attributes_modifier[fn];
572}
573
574static mapping _query_attributes_modifier()
575{
576 return deep_copy(attributes_modifier);
577}
578
579public int SetAttr(string attr, int val)
580{ closure filter_ldfied;
581
582 if ( filter_ldfied = symbol_function("_filterattr_"+attr, this_object()) )
583 val = funcall(filter_ldfied, val );
584
585 attributes[attr] = val;
586 UpdateAttributes();
587 return val;
588}
589
590public int SetAttribute(string attr, int val)
591{
592 return SetAttr(attr, val-used_attributes_offsets[attr]);
593}
594
595// Diese Funktion sollte zum Abfragen verwendet werden:
596public int QueryAttribute(string attr)
597{ int re;
598
599 re=attributes[attr]+used_attributes_offsets[attr];
600
601 if ( query_once_interactive(this_object()) && (re>30) )
602 re=30;
603
604 return re;
605}
606
607public int QueryRealAttribute(string attr)
608{
609 return attributes[attr];
610}
611
612public int SetRealAttribute(string attr, int val)
613{
614 return SetAttr(attr, val);
615}
616
617public int QueryAttributeOffset(string attr)
618{
619 return used_attributes_offsets[attr];
620}
621
622public status TestLimitViolation(mapping check)
623{
624 int k,test;
625 mixed* ind;
626
627 if(!check || !mappingp(check))
628 {
629 return 0;
630 }
631
632 test=0;
633 ind=m_indices(check);
634 for( k=sizeof(ind)-1 ; k>=0 ; k-- )
635 {
636 test+=check[ind[k]];
637 }
638
639 test+=cumulative_mod;
640 if(test>CUMULATIVE_ATTR_LIMIT)
641 {
642 return 1;
643 }
644
645 return 0;
646}
647
648/*
649 *------------------------------------------------------------
650 * attributes compatibility functions
651 */
652
653protected int _filterattr_str(int val)
654{
655 return ( (val<0) ? 0 : (val>20) ? 20 : val );
656}
657
658protected int _filterattr_dex(int val)
659{
660 return ( (val<0) ? 0 : (val>20) ? 20 : val );
661}
662
663protected int _filterattr_int(int val)
664{
665 return ( (val<0) ? 0 : (val>20) ? 20 : val );
666}
667
668protected int _filterattr_con(int val)
669{
670 return ( (val<0) ? 0 : (val>20) ? 20 : val );
671}