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