blob: ad90aa055d40a75674fbae582c9fa97d6f1b8971 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// living/skills_attributes.c - Verwaltung der Skillattribute von Lebewesen
4//
5// $Id: skills.c 6673 2008-01-05 20:57:43Z Zesstra $
6#pragma strict_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12inherit "/std/util/executer";
13
14#define NEED_PROTOTYPES
15#include <living/skill_attributes.h>
16#include <player/life.h>
17#include <thing/properties.h>
18#undef NEED_PROTOTYPES
19#include <thing/properties.h>
20#include <properties.h>
21#include <defines.h>
22
23//#define ZDEBUG(x) if (find_player("zesstra")) tell_object(find_player("zesstra"),x)
24#define ZDEBUG(x)
25//#define SASETLOG(x) log_file("SASET.LOG", x, 250000)
26
27//#define __DEBUG__
28
29// Variable fuer die Skill-Attribute, Datenstruktur:
30/* ([ SA_ATTR: ({Summe_Stat_Modifier, Zeitpunkt, AnzahlModifier, });
31 ([ ob1:value;duration,
32 ob2:value;duration, ...]); // stat. Modifier
33 ([ ob1:closure;duration,
34 ob2:closure;duration, ...]) // dyn. Modifier
35 ,
36 SA_ATTR2: ({...}); ([]); ([]),
37 ]) */
38private nosave mapping skillattrs;
39
40protected void create() {
41 Set(P_SKILL_ATTRIBUTES, SECURED, F_MODE_AS);
42}
43
44// von aussen Prop setzen ist nicht...
45mapping _set_skill_attr(mapping sa) {
46 return deep_copy(skillattrs);
47}
48// und auch beim Abfragen nur kopien liefern. ;-)
49mapping _query_skill_attr() {
50 return deep_copy(skillattrs);
51
52//TODO: Evtl. ext. Setzen von P_SKILL_ATTRIBUTE_OFFSETS mitloggen?
53}
54
55private void UpdateSACache(string *attrs) {
56#ifdef __DEBUG__
57 if (getuid(this_object()) == "zesstra")
58 ZDEBUG(sprintf("%O\n",skillattrs));
59#endif
60 if (!mappingp(skillattrs)) return;
61 if (!pointerp(attrs) || sizeof(attrs))
62 attrs = m_indices(skillattrs); // alle
63 // sonst schnittmenge aus existierenden und den uebergebenen.
64 attrs = m_indices(skillattrs) & attrs;
65
66 // und jetzt ueber alle gewuenschten SAs drueber
67 foreach(string attr : attrs) {
68 int *cache = skillattrs[attr, SAM_CACHE];
69 mapping stat = skillattrs[attr, SAM_STATIC];
70 mapping dyn = skillattrs[attr, SAM_DYNAMIC];
71 int sum = 0;
72 int timeout = __INT_MAX__;
73
74 // ueber stat. Mods iterieren und Aufsummieren, kleinsten Timeout
75 // ermitteln.
76 foreach(object ob, int value, int duration: stat) {
77 // gueltige Mods aufaddieren, abgelaufene rauswerfen
78 if (duration >= time()) {
79 sum += value;
80 if (duration < timeout)
81 timeout = duration;
82 }
83 else
84 m_delete(stat, ob); // ja, geht im foreach ;-)
85 }
86 // Ablaufzeiten der dyn. Mods pruefen, waere hier zwar nicht unbedingt
87 // noetig sondern koennte man ausschliesslich im QuerySkillAttribute()
88 // machen, aber dann waere die Ermittlung der Summe der Mods schwieriger.
89 foreach(object ob, closure value, int duration: dyn) {
90 if (duration < time())
91 m_delete(dyn, ob); // ungueltig, weg damit.
92 }
93 // gesamtzahl Mods?
94 cache[SAM_COUNT] = sizeof(stat) + sizeof(dyn);
95 if (!cache[SAM_COUNT]) {
96 // keine mods da, Submapping fuer dieses SA komplett loeschen.
97 m_delete(skillattrs, attr);
98 continue;
99 }
100 // sonst die anderen Cache-Werte setzen.
101 cache[SAM_SUM] = sum;
102 cache[SAM_CACHE_TIMEOUT] = timeout;
103 }
104 // wenn alle Mods geloescht wurden.
105 if (!sizeof(skillattrs)) skillattrs=0;
106#ifdef __DEBUG__
107 if (getuid(this_object()) == "zesstra")
108 ZDEBUG(sprintf("%O\n",skillattrs));
109#endif
110}
111
112private int InternalModifySkillAttribute(object caster, string atrname,
113 mixed value, int duration) {
114 int zeit = utime()[1];
115 int ticks = get_eval_cost();
116
117 // nur existierende SAs...
118 if (!stringp(atrname)
119 || member(VALID_SKILL_ATTRIBUTES, atrname) == -1)
120 return SA_MOD_INVALID_ATTR;
121
122 if (!objectp(caster)) return SA_MOD_INVALID_OBJECT;
123
124 if (!mappingp(skillattrs)) skillattrs=m_allocate(1,3);
125
126 if (!member(skillattrs, atrname)) {
127 skillattrs[atrname,SAM_CACHE] = ({0, 0, 0});
128 // die meisten Mods sind statisch, daher auf Verdacht hier fuer einen
129 // Eintrag Platz reservieren, aber nicht fuer den dyn. Teil.
130 skillattrs[atrname,SAM_STATIC] = m_allocate(1,2);
131 skillattrs[atrname,SAM_DYNAMIC] = m_allocate(0,2);
132 }
133 // pruefen, ob Maximalzahl an Eintraegen drin ist
134 else if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS) {
135 // letzte Chance: destructete Objekte drin?
136 skillattrs[atrname,SAM_CACHE][SAM_COUNT] =
137 sizeof(skillattrs[atrname,SAM_STATIC])
138 +sizeof(skillattrs[atrname,SAM_DYNAMIC]);
139 // es kann sein, dass noch abgelaufene Objekte drinstehen,
140 // aber die Pruefung ist mir gerade zu teuer. TODO
141 // nochmal gucken (rest vom cache wird ggf. unten geprueft.)
142 if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS)
143 return SA_TOO_MANY_MODS; // dann nicht.
144 }
145
146 // Dauer darf nur ein int sein.
147 if (!intp(duration))
148 raise_error(sprintf("Wrong argument 3 to ModifySkillAttribute: "
149 "expected 'int', got %.10O\n", duration));
150 // Zeitstempel ermitteln
151 duration += time();
152
153 // statischer oder dyn. Modifier?
154 if (intp(value)) {
155 // Mod darf nicht zu gross oder zu klein sein. TODO: Grenzen?
156 if (value < -1000)
157 return SA_MOD_TOO_SMALL;
158 else if (value > 1000)
159 return SA_MOD_TOO_BIG;
160 else if (!value)
161 return SA_MOD_INVALID_VALUE;
162 // jedes Objekt darf nur einen mod haben. Wenn dieses schon einen dyn.
163 // hat, muss der geloescht werden (stat. werden ja eh ersetzt).
164 if (member(skillattrs[atrname,SAM_DYNAMIC], caster))
165 m_delete(skillattrs[atrname,SAM_DYNAMIC], caster);
166 // sonst eintragen
167 skillattrs[atrname, SAM_STATIC] += ([caster: value; duration]);
168 }
169 else if (closurep(value)) {
170 // nur ein Mod pro Objekt, s.o.
171 if (member(skillattrs[atrname,SAM_STATIC], caster))
172 m_delete(skillattrs[atrname,SAM_STATIC], caster);
173 // direkt ohne weitere Pruefung eintragen
174 skillattrs[atrname, SAM_DYNAMIC] += ([caster: value; duration]);
175 }
176 else
177 raise_error(sprintf("Wrong argument 2 to ModifySkillAttribute(): "
178 "expected 'int' or 'closure', got %.10O\n",value));
179
180#ifdef SASETLOG
181 if (query_once_interactive(this_object()))
182 SASETLOG(sprintf("%s: %O, %s, %O, %O\n",
183 strftime("%y%m%d-%H%M%S"), this_object(), atrname, caster, value));
184#endif
185#ifdef SASTATD
186 object daemon;
187 if (query_once_interactive(ME)
188 && objectp(daemon=find_object(SASTATD)))
189 daemon->LogModifier(caster, atrname, value, duration);
190#endif
191 // noch den Cache fuer dieses SA neu berechnen
192 // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
193 // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
194 // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
195 // inkonsistent.
196 UpdateSACache( ({atrname}) );
197
198 ZDEBUG(sprintf("MSA: %O, Zeit: %d, Ticks: %d\n",
199 this_object(),
200 utime()[1]-zeit, ticks-get_eval_cost()));
201
202 return SA_MOD_OK;
203}
204
205public int ModifySkillAttribute(string atrname, mixed value,
206 int duration) {
207 return InternalModifySkillAttribute(
208 (extern_call()?previous_object():ME), atrname, value, duration);
209}
210
211public int RemoveSkillAttributeModifier(object caster, string attrname) {
212 if (!stringp(attrname) || !mappingp(skillattrs) || !objectp(caster)
213 || !member(skillattrs, attrname))
214 return SA_MOD_NOT_FOUND;
215 // TODO: Berechtigung pruefen. ;-)
216
217 if (member(skillattrs[attrname, SAM_STATIC], caster)) {
218 m_delete(skillattrs[attrname, SAM_STATIC], caster);
219 }
220 else if (member(skillattrs[attrname, SAM_DYNAMIC], caster)) {
221 m_delete(skillattrs[attrname, SAM_DYNAMIC], caster);
222 }
223 else
224 return SA_MOD_NOT_FOUND;
225
226 // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
227 // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
228 // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
229 // inkonsistent.
230 UpdateSACache( ({attrname}) );
231
232 return SA_MOD_REMOVED;
233}
234
235public int QuerySkillAttribute(string atrname)
236{
237 mixed offsets, attr;
238 int modsumme, qual, ret, cval;
239
240 if (!stringp(atrname)) // VALID_SKILL_ATTRIBUTES beruecksichtigen?
241 return 100;
242
243 // wenn nicht SA_QUALITY gefragt ist, erstmal jenes ermitteln, weil es den
244 // ersten Modifier auf alle anderen SAs darstellt. Sonst den Startwert fuer
245 // SA_QUALITY (100) modifiziert durch evtl. Todesfolgen ermitteln.
246 if ( atrname != SA_QUALITY )
247 qual = QuerySkillAttribute(SA_QUALITY);
248 else
249 // bei SA_QUALITY gehen die Todesfolgen ein
250 qual = to_int(100 * pow(0.9, death_suffering()/10.0));
251
252 // Die Offsets sind sozusagen der Basiswert der SAs. Als erstes verwursten,
253 // sofern vorhanden, nen int drinsteht und der offset != 0 ist.
254 if ( mappingp(offsets = Query(P_SKILL_ATTRIBUTE_OFFSETS))
255 && intp(attr=offsets[atrname])
256 && attr)
257 ret = attr;
258 else
259 ret = 100;
260
261 // wenn keine Mods gesetzt sind, wars das jetzt. ;-)
262 if ( !mappingp(skillattrs)
263 || !member(skillattrs, atrname) )
264 return (ret*qual)/100;
265
266 // wenn Cache der stat. Mods abgelaufen oder offenbar Objekte zerstoert
267 // wurden, muss der Cache neu berechnet werden.
268 if ( skillattrs[atrname,SAM_CACHE][SAM_CACHE_TIMEOUT] < time()
269 || sizeof(skillattrs[atrname,SAM_STATIC])
270 +sizeof(skillattrs[atrname,SAM_DYNAMIC]) !=
271 skillattrs[atrname,SAM_CACHE][SAM_COUNT] )
272 {
273 UpdateSACache( ({atrname}) );
274 // UpdateSACache() loescht uU das SA-Mapping oder Eintraege daraus.
275 if ( !mappingp(skillattrs)
276 || !member(skillattrs, atrname) )
277 return (ret*qual)/100;
278 }
279 // Summe der statischen Mods.
280 modsumme = skillattrs[atrname,SAM_CACHE][SAM_SUM];
281
282 // TODO! Evtl. andere Daten als ME an die Funktion uebergeben
283 // TODO! bereits nach Addition des Funktionsrueckgabewertes pruefen, ob der
284 // Wertebereich des SA-Modifiers ueberschritten ist, oder freien
285 // Wertebereich erlauben und erst am Ende deckeln (aktuelle Variante)
286 // Dynamische Modifier auswerten
287 foreach( object ob, closure cl, int duration:
288 skillattrs[atrname,SAM_DYNAMIC] )
289 {
290 if ( duration > time() // Noch nicht abgelaufen und
291 && intp(cval=funcall(cl, ME)) ) // Funktion liefert int zurueck
292 modsumme += cval;
293 else {
294 m_delete(skillattrs[atrname,SAM_DYNAMIC], ob);
295 skillattrs[atrname,SAM_CACHE][SAM_COUNT]--;
296 }
297 }
298 ret = ((ret+modsumme)*qual)/100;
299 if ( ret < 10 )
300 ret = 10;
301 else if ( ret > 1000 )
302 ret = 1000;
303
304 return ret;
305}
306
307public varargs mapping QuerySkillAttributeModifier(object caster,
308 string *attrnames) {
309
310 // auf abgelaufene Modifikatoren pruefen
311 if (!pointerp(attrnames))
312 UpdateSACache( ({}) );
313 else
314 UpdateSACache(attrnames);
315
316 if (!mappingp(skillattrs))
317 return ([]);
318 if (!pointerp(attrnames) || !sizeof(attrnames))
319 attrnames = m_indices(skillattrs); // alle durchsuchen
320 else // schnittmenge der gew. und vorhandenen bilden
321 attrnames = m_indices(skillattrs) & attrnames;
322
323 mapping res=m_allocate(sizeof(attrnames), 1);
324
325 foreach(string atr: attrnames) {
326 res[atr] = m_allocate(5, 2); // mal fuer 5 Werte Platz reservieren
327 if (!objectp(caster)) {
328 // wenn kein bestimmter caster angefragt ist, alle mods liefern
329 foreach(object c, int value, int dur: skillattrs[atr, SAM_STATIC]) {
330 res[atr] += ([c: value; dur]);
331 }
332 foreach(object c, closure value, int dur: skillattrs[atr, SAM_DYNAMIC]) {
333 res[atr] += ([c: value; dur]);
334 }
335 }
336 else {
337 // sonst nur den Mod von caster
338 if (member(skillattrs[atr, SAM_STATIC], caster)) {
339 res[atr] += ([caster:
340 skillattrs[atr, SAM_STATIC][caster, SAM_VALUE];
341 skillattrs[atr, SAM_STATIC][caster, SAM_DURATION]
342 ]);
343 }
344 else if (member(skillattrs[atr, SAM_DYNAMIC], caster)) {
345 res[atr] += ([caster:
346 skillattrs[atr, SAM_DYNAMIC][caster, SAM_VALUE];
347 skillattrs[atr, SAM_DYNAMIC][caster, SAM_DURATION]
348 ]);
349 }
350 }
351 }
352 return res;
353}
354
355// Kompatibilitaetsfunktion mit altem Interface. Ist nur ein Wrapper, der
356// value umrechnet und 'alte' Rueckgabewerte liefert.
357
358public varargs int ModifySkillAttributeOld(object caster, string atrname,
359 int value, int duration, mixed fun) {
360 int res;
361 // Caller ermitteln
362 if (extern_call()) caster=previous_object();
363 else caster=ME;
364
365 // Closures koennen via ModifySkillAttributeOld() nicht mehr gesetzt werden,
366 // da deren Rueckgabewert nicht sinnvoll umgerechnet werden koennen. (Man
367 // weiss nicht, ob es eine neue oder alte Closure ist.)
368 if (pointerp(fun) || closurep(fun))
369 raise_error(sprintf("Closures for SA modifiers can't be set by "
370 "ModifySkillAttributeOld()! Use ModifySkillAttribute()!\n"));
371
372 res = InternalModifySkillAttribute(caster, atrname, value-100, duration);
373 // die alte funktion hatte nur 0 fuer ungueltigen Wert und < 0 fuer zu
374 // kleines Level als Rueckgabewert. Zu kleines Level gibt nicht mehr, also
375 // bleibt nur 0 als Sammel-Fehlercode uebrig. *seufz*
376 if (res < 0) return 0;
377 return res;
378}
379