MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | // 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 | |
| 12 | inherit "/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 | ]) */ |
| 38 | private nosave mapping skillattrs; |
| 39 | |
| 40 | protected void create() { |
| 41 | Set(P_SKILL_ATTRIBUTES, SECURED, F_MODE_AS); |
| 42 | } |
| 43 | |
| 44 | // von aussen Prop setzen ist nicht... |
| 45 | mapping _set_skill_attr(mapping sa) { |
| 46 | return deep_copy(skillattrs); |
| 47 | } |
| 48 | // und auch beim Abfragen nur kopien liefern. ;-) |
| 49 | mapping _query_skill_attr() { |
| 50 | return deep_copy(skillattrs); |
| 51 | |
| 52 | //TODO: Evtl. ext. Setzen von P_SKILL_ATTRIBUTE_OFFSETS mitloggen? |
| 53 | } |
| 54 | |
| 55 | private 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 | |
| 112 | private 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 | |
| 205 | public int ModifySkillAttribute(string atrname, mixed value, |
| 206 | int duration) { |
| 207 | return InternalModifySkillAttribute( |
| 208 | (extern_call()?previous_object():ME), atrname, value, duration); |
| 209 | } |
| 210 | |
| 211 | public 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 | |
| 235 | public 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 | |
| 307 | public 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 | |
| 358 | public 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 | |