| // MorgenGrauen MUDlib |
| // |
| // container/vitems.c -- managing virtually present automatic items |
| // |
| #pragma strict_types,rtt_checks, range_check, pedantic |
| #pragma no_clone |
| |
| #include <properties.h> |
| #define NEED_PROTOTYPES |
| #include <thing/properties.h> |
| #include <container/vitems.h> |
| |
| // Werte im Mapping in P_VITEMS: |
| #define VI_PATH 0 // Pfad zur BP |
| #define VI_REFRESH 1 // Refresheinstellung, s. AddItem |
| #define VI_OBJECT 2 // Aktuelles Objekt des VItem |
| #define VI_PROPS 3 // Props fuer das echte Objekt |
| #define VI_SHADOW_PROPS 4 // Props fuer den shadow/vitem_proxy |
| #define VI_LAST_OBJ 5 // letztes mitgenommenes Objekt |
| #define VI_LENGTH 6 |
| |
| // VI_REFRESH hat aehnliche Bedeutung wie bei AddItem, mit einer Besonderheit: |
| // intern wird in Form eines negativen Wertes kodiert, ob das VItem gerade neu |
| // erzeugt werden darf (negativ: ja, positiv: nein). |
| |
| /* Mehr zu VI_REFRESH: |
| * vitems raeumen sich ggf. selber weg. D.h. wenn der Clone des vitems nicht |
| * mehr ist, heisst das nicht, dass es jemand mitgenommen hat. In dem Fall |
| * muss es sofort (wenn seine Praesenz abgefragt wird) neu erzeugt werden. |
| * Die Logik ist wie folgt: |
| * Der Standardzustand von VI_REFRESH ist ein negierter Wert. Solange dies der |
| * Fall ist, wird ein vitem *immer* erzeugt, sofern es abgefragt wird, aber |
| * nicht da ist. |
| * Wird ein vitem hingegen bewegt/mitgenommen, wird VI_REFRESH auf den |
| * positiven Wert geaendert. In diesem Fall wird es NICHT mehr neu erzeugt, |
| * wenn es nicht als vitem praesent ist. Erst im naechsten reset() wird |
| * geprueft, ob es in Zukunft wieder neu erzeugt werden darf. Wenn ja, wird |
| * der Zahlenwert wieder negiert. |
| * Es ist Absicht, dass VI_REFRESH_ALWAYS auch nur maximal einmal pro reset() |
| * *mitgenommen* werden darf. |
| */ |
| |
| |
| //protected void create() |
| //{ |
| //} |
| |
| public varargs void RemoveVItem(string key) |
| { |
| mixed vitems=QueryProp(P_VITEMS); |
| if (mappingp(vitems) && member(vitems, key)) |
| { |
| // Es wird auch zerstoert, wenn das genommene Objekt gerade im Raum |
| // rumliegt (weil Spieler es hat fallen lassen etc.) |
| if (vitems[key, VI_OBJECT]) |
| vitems[key, VI_OBJECT]->remove(1); |
| if (vitems[key, VI_LAST_OBJ] |
| && environment(vitems[key, VI_LAST_OBJ]) == this_object()) |
| vitems[key, VI_LAST_OBJ]->remove(1); |
| |
| m_delete(vitems, key); |
| SetProp(P_VITEMS, vitems); |
| } |
| } |
| |
| // TODO: braucht es dynamische refresh, shadowprops und props? |
| public varargs void AddVItem(string key, int refresh, mapping shadowprops, |
| string path, mapping props) |
| { |
| if (!sizeof(key)) |
| return; |
| // Wenn path gegeben, muss es eine ladbare Blueprint sind |
| if (sizeof(path) && !load_object(path)) |
| return; |
| if (!sizeof(path)) |
| { |
| if (mappingp(props)) |
| raise_error("Reine vitems erlauben keine <props>\n"); |
| } |
| |
| refresh ||= VI_REFRESH_NONE; |
| shadowprops ||= ([]); |
| // Wenn reines vItem und keine IDs gesetzt, wird <key> als ID verwendet, |
| // irgendwie muss es ja ansprechbar sein. (Wenn es ein Objekt mit Templat |
| // ist, hat es normalerweise die IDs aus dem Templat. Wenn man das nicht |
| // will, muss man es mit gezielter Angabe von P_IDS in den Shadowprops |
| // ueberschreiben.) Gleiches fuer P_NAME (ohne ist ein "Ding") und P_SHORT |
| // (ohne P_SHORT ist es unsichtbar) |
| if (!path) |
| { |
| if (!member(shadowprops, P_IDS)) |
| shadowprops[P_IDS] = ({key}); |
| if (!member(shadowprops, P_NAME)) |
| shadowprops[P_NAME] = capitalize(key); |
| if (!member(shadowprops, P_SHORT)) |
| shadowprops[P_SHORT] = capitalize(key); |
| } |
| mixed vitems=QueryProp(P_VITEMS); |
| if (!mappingp(vitems)) |
| vitems = m_allocate(1, VI_LENGTH); |
| vitems[key, VI_PATH] = path; |
| vitems[key, VI_REFRESH] = negate(refresh); |
| vitems[key, VI_PROPS] = props; |
| vitems[key, VI_SHADOW_PROPS] = shadowprops; |
| SetProp(P_VITEMS, vitems); |
| } |
| |
| |
| private void configure_object(object ob, mapping props) |
| { |
| foreach (string k, mixed v : props) |
| { |
| int reset_prop; |
| if (k[0] == VI_RESET_PREFIX) |
| { |
| reset_prop=1; |
| k=k[1..]; |
| } |
| switch(k) |
| { |
| case P_READ_DETAILS: |
| if (reset_prop) ob->RemoveReadDetail(0); |
| walk_mapping(v, "AddReadDetail", ob); |
| break; |
| case P_DETAILS: |
| if (reset_prop) ob->RemoveDetail(0); |
| walk_mapping(v, "AddDetail", ob); |
| break; |
| case P_SMELLS: |
| if (reset_prop) ob->RemoveSmells(0); |
| walk_mapping(v, "AddSmells", ob); |
| break; |
| case P_SOUNDS: |
| if (reset_prop) ob->RemoveSounds(0); |
| walk_mapping(v, "AddSounds", ob); |
| break; |
| case P_TOUCH_DETAILS: |
| if (reset_prop) ob->RemoveTouchDetail(0); |
| walk_mapping(v, "AddTouchDetail", ob); |
| break; |
| case P_IDS: |
| if (reset_prop) ob->SetProp(P_IDS, v); |
| else ob->AddId(v); |
| case P_CLASS: |
| if (reset_prop) ob->SetProp(P_CLASS, v); |
| else ob->AddClass(v); |
| case P_ADJECTIVES: |
| if (reset_prop) ob->SetProp(P_ADJECTIVES, v); |
| else ob->AddAdjective(v); |
| break; |
| |
| // Alle anderen Properties stumpf setzen. |
| default: |
| ob->SetProp(k, v); |
| } |
| } |
| } |
| |
| // Clont ein vitem, falls noetig. |
| // Nebeneffekt ist aber in jedem Fall auch, dass ein nicht mehr virtuell |
| // anwesendes Objekt als VI_LAST_OBJ gespeichert und VI_OBJECT geloescht wird. |
| private void check_vitem(string key, string path, int refresh, |
| object obj, mapping props, mapping shadow_props, |
| object last_obj) |
| { |
| // Ist es noch da? Ein vItem ist "da", wenn obj auf ein gueltiges Objekt |
| // zeigt, welches *KEIN* Environment hat. (Hat es ein Environment, wurde es |
| // mitgenommen.) |
| if (obj) |
| { |
| if (!environment(obj)) |
| return; |
| // wenn es mitgenommen wurde, ist obj in jedem Fall kein vItem mehr: |
| // (eigentlich wird das in VItemMoved schon gemacht, aber mal fuer den |
| // Fall, dass jemand den Shadow hart entsorgt hat o.ae.) |
| last_obj = obj; |
| obj = 0; |
| // und wird ggf. unten neu erzeugt. |
| } |
| |
| if (refresh < 0) |
| { |
| object sh; |
| if (path) |
| { |
| obj=clone_object(path); |
| obj->SetAutoObject(1); |
| if (mappingp(props)) |
| configure_object(obj, props); |
| // Schatten erzeugen, welcher die Beschreibung des Objekts im Container nach |
| // den Props in shadow_props abaendert. |
| sh = clone_object("/obj/vitem_shadow"); |
| sh->set_shadow(obj, shadow_props); |
| } |
| else |
| { |
| obj=clone_object("/obj/vitem_proxy"); |
| configure_object(obj, shadow_props); |
| // no shadow needed in this case. |
| } |
| } |
| } |
| |
| // Erzeugt Instanzen der vItems (sofern noetig und erlaubt durch |
| // Refresh-Parameter). |
| private mixed CheckVItems() |
| { |
| mixed vitems=QueryProp(P_VITEMS); |
| if (!mappingp(vitems)) |
| vitems = m_allocate(0, VI_LENGTH); |
| walk_mapping(vitems, #'check_vitem); |
| return vitems; |
| } |
| |
| // Liefert alle in diesem Raum virtuell anwesenden Items |
| public object *GetVItemClones() |
| { |
| mapping vitems = CheckVItems(); |
| return filter(m_values(vitems, VI_OBJECT), #'objectp); |
| } |
| |
| public object present_vitem(string complex_desc) |
| { |
| foreach(object o : GetVItemClones()) |
| { |
| if (o->id(complex_desc)) |
| return o; |
| } |
| return 0; |
| } |
| |
| // wird aus dem Shadow fuer das VItem gerufen, wenn es genomment etc. wird. |
| // In dem Fall wird das Refresh "gesperrt", d.h. es wird fruehestens nach dem |
| // naechsten Reset wieder neu erzeugt - sofern im naechsten Reset die |
| // Vorraussetzungen erfuellt sind. |
| public void VItemMoved(object ob) |
| { |
| if (load_name(previous_object()) == "/obj/vitem_shadow") |
| { |
| mapping vitems = QueryProp(P_VITEMS); |
| if (!mappingp(vitems)) |
| return; |
| // passendes vitem suchen |
| foreach(string key, string path, int refresh, object o, mapping props, |
| mapping shadow_props, object last_obj: &vitems) |
| { |
| if (ob != o) |
| continue; |
| else |
| { |
| // mitgenommenes Objekt merken und vitem-objekt loeschen |
| last_obj = o; |
| o = 0; |
| // Sperren gegen sofortiges Neuerzeugen, wenn refresh nicht |
| // VI_REFRESH_INSTANT ist. |
| if (refresh != VI_REFRESH_INSTANT) |
| refresh = negate(refresh); |
| break; |
| } |
| } |
| } |
| } |
| |
| void reset() |
| { |
| mapping vitems=QueryProp(P_VITEMS); |
| if (!mappingp(vitems)) |
| return; |
| foreach(string key, string path, int refresh, object obj, |
| mapping props, mapping shadow_props, object last_obj : &vitems) |
| { |
| // Wenn negativ (d.h. vItem wurde noch nicht mitgenommen), 0 oder |
| // REFRESH_NONE, muss hier nix gemacht werden. |
| if (refresh <= REFRESH_NONE) |
| continue; |
| |
| // Wenn last_obj nicht mehr existiert, darf (ausgenommen natuerlich |
| // REFFRESH_NONE) immer neu erzeugt werden. |
| if (!last_obj) |
| { |
| refresh=negate(refresh); |
| continue; |
| } |
| |
| // restliche Faelle |
| switch(refresh) |
| { |
| case VI_REFRESH_MOVE_HOME: |
| // Wenn das Objekt nicht mehr als vItem hier ist (auch wenn es hier im |
| // Raum liegt!), wird es heim bewegt (sprich: zerstoert und neues |
| // vitem). |
| // (Da man hier nur hinkommt, wenn es mitgenommen wurde (refresh > 0), |
| // wird es immer refresht... |
| // Zu beachten: es soll auch nicht hier in diesem Container rumliegen |
| // nach dem Heimbewegen, also zerstoeren! |
| last_obj->remove(1); |
| // Fallthrough |
| case VI_REFRESH_REMOVE: |
| // wenn nicht mehr als vItem hier ist (d.h. auch wenn es hier im Raum |
| // rumliegt!) darf es neu erzeugt werden. |
| // (Hierher kommt die Ausfuehrung nur her, wenn es mitgenommen |
| // wurde, d.h. letztendlich: immer. d.h. Fallthrough.) |
| case VI_REFRESH_ALWAYS: |
| // neu erzeugen |
| refresh=negate(refresh); |
| break; |
| } |
| } |
| } |
| |