blob: 90027a4a54c89d01b481be1d1da3f574759c085a [file] [log] [blame]
// MorgenGrauen MUDlib
//
// living/attributes.c -- attributes for living objects
//
// $Id: attributes.c 8375 2013-02-12 21:52:58Z Zesstra $
#include <sys_debug.h>
// attribute handling
//
// filter_ldfied: str, dex, con, int
//
// In den Attributen und Abilites werden Rassenspzifische und erworbene
// Faehigkeiten (mit autoload-Eigenschaft) abgepseichert.
//
// Funktionen:
// SetAttribute( attr, val ) (Erzeuge und) setze das Attribut auf val.
// QueryAttribute( attr ) Gebe den Wert des Attributes zurueck.
//
// Wenn ein Objekt eine Funktion _filterattr_<name> beitzt, wird beim Setzen
// des Attributes <name>, der vorgeschlagene Wert uebergeben und der von
// dieser Funktion zurueckgegebene gesetzt. (fuer ueberpruefungszwecke)
// Gleiches gilt fuer _filterabil_<name>.
#pragma strict_types
#pragma save_types
#pragma range_check
#pragma no_clone
#pragma pedantic
#define NEED_PROTOTYPES
#include <thing/properties.h>
#include <attributes.h>
#include <player/gmcp.h>
#undef NEED_PROTOTYPES
#include <config.h>
#include <properties.h>
mapping attributes; // Dies sind die mit ZTs veraenderbaren Attribute
mapping attributes_modifier; // Modifier sollen gespeichert werden
nosave mixed* attributes_timed_mods; //P_TIMED_ATTR_MOD
nosave mapping attributes_offsets; // Offsets NICHT speichern!
nosave mapping used_attributes_offsets; // Zur Beschleunigung der Berechnung
nosave object* all_modifiers; // objekte mit P_X/M_ATTR_MOD, P_X/M_HEALTH_MOD
nosave object* invalid_modifiers; // objekte welche das limit ueberschreiten
nosave int cumulative_mod; // bilanz der P_X/M_ATTR_MOD
nosave int hp_off;
nosave int sp_off;
nomask public int SetTimedAttrModifier(string key, mapping modifier,
int outdated, object dependent, mixed notify) {
if(!key || key=="" || !modifier || (outdated>0 && outdated<time()) ||
(notify && !stringp(notify) && !objectp(notify)) ) {
return TATTR_INVALID_ARGS;
}
if(member(attributes_timed_mods[TATTR_ENTRIES],key)) {
// change entry
attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]=modifier;
attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED]=outdated;
attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT]=dependent;
attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY]=notify;
}
else {
// add entry
attributes_timed_mods[TATTR_ENTRIES]+=([key:modifier;outdated;dependent;notify]);
}
// add outdate
if(outdated>0 && member(attributes_timed_mods[TATTR_OUTDATE],outdated)==-1)
{
attributes_timed_mods[TATTR_OUTDATE]+=({outdated});
attributes_timed_mods[TATTR_OUTDATE]=
sort_array(attributes_timed_mods[TATTR_OUTDATE],#'<);
}
// add dependent
if(objectp(dependent))
{
if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
{
attributes_timed_mods[TATTR_DEPENDENTS][key]=dependent;
}
else
{
attributes_timed_mods[TATTR_DEPENDENTS]+=([key:dependent]);
}
}
else
{
// remove previously set dependent
if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
{
attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
}
}
UpdateAttributes();
return TATTR_OK;
}
nomask public mapping QueryTimedAttrModifier(string key)
{
int outdated;
object dependent;
mixed notify;
mapping mod;
if(!key || key=="")
{
return ([]);
}
if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
{
return ([]);
}
mod=deep_copy(attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]);
outdated=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED];
dependent=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT];
notify=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY];
return ([key:mod;outdated;dependent;notify ]);
}
nomask public int DeleteTimedAttrModifier(string key)
{
if(!key || key=="")
{
return TATTR_INVALID_ARGS;
}
if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
{
return TATTR_NO_SUCH_MODIFIER;
}
attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
attributes_timed_mods[TATTR_ENTRIES]-=([key]);
UpdateAttributes();
return TATTR_OK;
}
nomask protected void attribute_hb()
{
int now,i,k,update,outdated;
string* keys;
mapping tonotify;
// initialize
now=time();
tonotify=([]);
keys=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
// delete outdated
for(i=sizeof(attributes_timed_mods[TATTR_OUTDATE])-1;i>=0;i--)
{
outdated=attributes_timed_mods[TATTR_OUTDATE][i];
if(outdated>now)
{
break;
}
for(k=sizeof(keys)-1;k>=0;k--)
{
if(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_OUTDATED]==outdated)
{
// bei fehlendem notifier wurde das zum verhaengnis
// dank an gloinson
/*
if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]))
{
*/
tonotify+=([keys[k]:attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]]);
//}
attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[k]]);
attributes_timed_mods[TATTR_ENTRIES]-=([keys[k]]);
keys-=({keys[k]});
}
}
attributes_timed_mods[TATTR_OUTDATE]-=({outdated});
}
// delete depending
keys=m_indices(attributes_timed_mods[TATTR_DEPENDENTS]);
for(i=sizeof(keys)-1;i>=0;i--)
{
if(!objectp(attributes_timed_mods[TATTR_DEPENDENTS][keys[i]]))
{
// siehe oben
/*
if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]))
{
*/
tonotify+=([keys[i]:attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]]);
//}
attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[i]]);
attributes_timed_mods[TATTR_ENTRIES]-=([keys[i]]);
keys-=({keys[i]});
}
}
// update
if(sizeof(tonotify))
{
UpdateAttributes();
call_out(#'notifyExpiredModifiers,0,tonotify);
}
}
nomask protected void notifyExpiredModifiers(mapping nots)
{
int i;
string* keys;
while(remove_call_out(#'notifyExpiredModifiers)!=-1);
if(!nots)
{
return;
}
keys=m_indices(nots);
for(i=sizeof(nots)-1;i>=0;i--)
{
if(nots[keys[i]] &&
( objectp(nots[keys[i]])||stringp(nots[keys[i]]) ) )
{
call_other(nots[keys[i]],"NotifyTimedAttrModExpired",keys[i]);
}
}
}
// invalide modifier benachrichtigen
nomask protected void notifyInvalidModifiers() {
while(remove_call_out(#'notifyInvalidModifiers)!=-1);
if(!invalid_modifiers) {
invalid_modifiers=({});
}
call_other(invalid_modifiers,"NotifyXMAttrModLimitViolation");
}
// welche Modifier / modif. Objekte wirken nun?
protected nomask void calculate_valid_modifiers() {
closure qp;
mapping res;
string key;
int wert;
// Unterscheidung Bonus <-> Malus, weil der Malus voll eingehen soll
int hp_malus, sp_malus;
used_attributes_offsets=([]);
cumulative_mod=0;
hp_off=sp_off=0;
invalid_modifiers=({});
// rassenspezifische boni P_ATTRIBUTES_OFFSETS
if ( mappingp(attributes_offsets) )
used_attributes_offsets+=attributes_offsets;
if (!pointerp(all_modifiers) || !sizeof(all_modifiers)) {
// in diesem Fall koennen wir hier direkt mit dieser Funktion Schluss
// machen. ;-)
return;
}
else
all_modifiers-=({0}); //zerstoerte Objekte rauswerfen.
// einmal ueber alle modifizierenden Objekt iterieren und aufaddieren
foreach(object ob: all_modifiers) {
qp = symbol_function("QueryProp",ob);
if (!objectp(ob) || environment(ob)!=this_object()) {
all_modifiers-=({ob});
continue;
}
// ext. Attribut-Modifier
if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
foreach(key, wert: res) {
cumulative_mod+=wert;
}
}
// magic Attribut-Modifier
if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
&& ( funcall(qp,P_WORN)
|| funcall(qp,P_WIELDED) ) ) {
foreach(key, wert: res) {
cumulative_mod+=wert;
}
}
// Magic Health-Modifier
if ( mappingp(res=funcall(qp,P_M_HEALTH_MOD))
&& ( funcall(qp,P_WORN)
|| funcall(qp,P_WIELDED) ) ) {
if (res[P_HP] < 0)
hp_malus += res[P_HP];
else
hp_off+=res[P_HP];
if (res[P_SP] < 0)
sp_malus += res[P_SP];
else
sp_off+=res[P_SP];
}
// external Health Modifier
if ( mappingp(res=funcall(qp,P_X_HEALTH_MOD)) ) {
if (res[P_HP] < 0)
hp_malus += res[P_HP];
else
hp_off+=res[P_HP];
if (res[P_SP] < 0)
sp_malus += res[P_SP];
else
sp_off+=res[P_SP];
}
} // Ende 1. foreach()
// und nochmal, soviele Objekt wieder rausschmeissen, bis das Limit wieder
// unterschritten wird. (Verbesserungsvorschlaege erwuenscht.) :-(
foreach(object ob: all_modifiers) {
qp = symbol_function("QueryProp",ob);
if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {
invalid_modifiers+=({ob});
foreach(key, wert: res) {
cumulative_mod-=wert;
}
}
else {
add_offsets(res);
}
}
if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
&& ( funcall(qp,P_WORN)
|| funcall(qp,P_WIELDED) ) ) {
if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {
if(member(invalid_modifiers,ob)==-1) {
invalid_modifiers+=({ob});
}
foreach(key, wert: res) {
cumulative_mod-=wert;
}
}
else {
add_offsets(res);
}
}
}
// HEALTH_MOD werden durch eine Formel 'entschaerft', damit man nur schwer
// das Maximum von 150 erreichen kann. (Formel von Humni, beschlossen auf 3.
// EM-Treffen)
// Mali gehen aber voll ein
hp_off = (int)(150 - (150*150.0/(150 + hp_off))) + hp_malus;
sp_off = (int)(150 - (150*150.0/(150 + sp_off))) + sp_malus;
/* alte Version
hp_off += hp_malus;
sp_off += sp_malus;
*/
// notify invalid modifiers
if(sizeof(invalid_modifiers)>0) {
call_out(#'notifyInvalidModifiers,0);
}
}
// abmelden eines modifiers
nomask public void deregister_modifier(object modifier)
{
if (!pointerp(all_modifiers)) {
return;
}
// valid object?
if (!objectp(modifier) || member(all_modifiers,modifier)==-1) {
return;
}
all_modifiers-=({modifier});
if (invalid_modifiers) {
invalid_modifiers-=({modifier});
}
}
// anmelden eines modifiers
nomask public void register_modifier(object modifier) {
closure qp;
if (!pointerp(all_modifiers)) {
all_modifiers=({});
}
// valid object?
if (!objectp(modifier) || environment(modifier)!=this_object() ||
member(all_modifiers,modifier)!=-1) {
return;
}
qp = symbol_function("QueryProp",modifier);
// modifier after all? Die P_M_* muessen getragen/gezueckt sein.
if(mappingp(funcall(qp,P_X_ATTR_MOD)) ||
mappingp(funcall(qp,P_X_HEALTH_MOD)) ||
((mappingp(funcall(qp,P_M_ATTR_MOD)) ||
mappingp(funcall(qp,P_M_HEALTH_MOD))) &&
(funcall(qp,P_WORN) || funcall(qp,P_WIELDED)) ) ) {
all_modifiers+=({modifier});
}
}
protected void add_offsets(mapping arr) {
mixed *ind;
int i;
if ( !mappingp(arr) )
return;
foreach(string key, int wert: arr) {
used_attributes_offsets[key]+=wert;
}
}
public void UpdateAttributes() {
mixed *ind;
int i;
// alle gueltigen Modifier ermitteln aus Objekten im Inventar.
calculate_valid_modifiers();
// persistente modifier drauf (frosch zb)
// aus P_ATTRIBUTES_MODIFIERS
if ( mappingp(attributes_modifier) ) {
foreach(mixed key, mapping wert: attributes_modifier) {
add_offsets(wert); // Modifier addieren...
}
}
// timed modifier drauf aus P_TIMED_ATTR_MOD
ind=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
for ( i=sizeof(ind)-1 ; i>=0 ; i-- ) {
// Modifier addieren...
add_offsets(attributes_timed_mods[TATTR_ENTRIES][ind[i],0]);
}
// Bei Monstern werden die HP/SP ueblicherweise selbst gesetzt
if ( !query_once_interactive(this_object()))
return;
SetProp(P_MAX_HP, QueryAttribute(A_CON)*8+42+hp_off);
SetProp(P_MAX_SP, QueryAttribute(A_INT)*8+42+sp_off);
if(QueryProp(P_HP)>QueryProp(P_MAX_HP)) {
SetProp(P_HP,QueryProp(P_MAX_HP));
}
if(QueryProp(P_SP)>QueryProp(P_MAX_SP)) {
SetProp(P_SP,QueryProp(P_MAX_SP));
}
GMCP_Char( ([ A_INT: QueryAttribute(A_INT),
A_DEX: QueryAttribute(A_DEX),
A_STR: QueryAttribute(A_STR),
A_CON: QueryAttribute(A_CON) ]) );
}
protected void create() {
hp_off=sp_off=0;
used_attributes_offsets=([]);
all_modifiers=({});
invalid_modifiers=({});
cumulative_mod=0;
Set(P_ATTRIBUTES_MODIFIER, attributes_modifier=([])); // nicht geschuetzt
Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=([]));
Set(P_ATTRIBUTES_OFFSETS, PROTECTED, F_MODE);
Set(P_ATTRIBUTES, attributes=([]));
Set(P_ATTRIBUTES, PROTECTED, F_MODE);
Set(P_TIMED_ATTR_MOD, attributes_timed_mods=({ ({}),([]),([]) }));
Set(P_TIMED_ATTR_MOD, NOSETMETHOD|SECURED, F_MODE_AS);
}
static mixed _query_timed_attr_mod() {
mixed ret;
return Set(P_TIMED_ATTR_MOD,
({attributes_timed_mods[0],
deep_copy(attributes_timed_mods[1]),
deep_copy(attributes_timed_mods[2])}));
}
static mapping _set_attributes(mapping arr)
{
Set(P_ATTRIBUTES, attributes=arr);
UpdateAttributes();
return arr;
}
static mapping _query_attributes()
{
return deep_copy(Set(P_ATTRIBUTES, attributes));
}
static mapping _set_attributes_offsets(mapping arr)
{
Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=arr);
UpdateAttributes();
return attributes_offsets;
}
static mapping _query_attributes_offsets()
{
return deep_copy(Set(P_ATTRIBUTES_OFFSETS, attributes_offsets));
}
static mixed _set_attributes_modifier(mixed arr)
{ string fn;
mixed pre;
mapping map_ldfied;
if ( pointerp(arr) && (sizeof(arr)>=2) )
{
pre=arr[0];
map_ldfied=arr[1];
}
else
{
pre=previous_object();
map_ldfied=arr;
}
if ( objectp(pre) )
fn=old_explode(object_name(pre),"#")[0];
else
fn=pre;
if ( !stringp(fn) )
return 0;
// wenn Modifier kein mapping oder ein leeres Mapping: loeschen
if ( !mappingp(map_ldfied) || !sizeof(map_ldfied))
m_delete(attributes_modifier,fn);
else
attributes_modifier[fn]=map_ldfied;
Set(P_ATTRIBUTES_MODIFIER, attributes_modifier);
UpdateAttributes();
return attributes_modifier[fn];
}
static mapping _query_attributes_modifier()
{
return deep_copy(attributes_modifier);
}
public int SetAttr(string attr, int val)
{ closure filter_ldfied;
if ( filter_ldfied = symbol_function("_filterattr_"+attr, this_object()) )
val = funcall(filter_ldfied, val );
attributes[attr] = val;
UpdateAttributes();
return val;
}
public int SetAttribute(string attr, int val)
{
return SetAttr(attr, val-used_attributes_offsets[attr]);
}
// Diese Funktion sollte zum Abfragen verwendet werden:
public int QueryAttribute(string attr)
{ int re;
re=attributes[attr]+used_attributes_offsets[attr];
if ( query_once_interactive(this_object()) && (re>30) )
re=30;
return re;
}
public int QueryRealAttribute(string attr)
{
return attributes[attr];
}
public int SetRealAttribute(string attr, int val)
{
return SetAttr(attr, val);
}
public int QueryAttributeOffset(string attr)
{
return used_attributes_offsets[attr];
}
public status TestLimitViolation(mapping check)
{
int k,test;
mixed* ind;
if(!check || !mappingp(check))
{
return 0;
}
test=0;
ind=m_indices(check);
for( k=sizeof(ind)-1 ; k>=0 ; k-- )
{
test+=check[ind[k]];
}
test+=cumulative_mod;
if(test>CUMULATIVE_ATTR_LIMIT)
{
return 1;
}
return 0;
}
/*
*------------------------------------------------------------
* attributes compatibility functions
*/
protected int _filterattr_str(int val)
{
return ( (val<0) ? 0 : (val>20) ? 20 : val );
}
protected int _filterattr_dex(int val)
{
return ( (val<0) ? 0 : (val>20) ? 20 : val );
}
protected int _filterattr_int(int val)
{
return ( (val<0) ? 0 : (val>20) ? 20 : val );
}
protected int _filterattr_con(int val)
{
return ( (val<0) ? 0 : (val>20) ? 20 : val );
}