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