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