blob: fbee29bf1f83a3e711770d564e87abea6b8a687f [file] [log] [blame]
Zesstra44030452018-11-12 22:34:02 +01001// MorgenGrauen MUDlib
2//
3// container/vitems.c -- managing virtually present automatic items
4//
5#pragma strict_types,rtt_checks, range_check, pedantic
6#pragma no_clone
7
8#include <properties.h>
9#define NEED_PROTOTYPES
10#include <thing/properties.h>
11#include <container/vitems.h>
12
13// Werte im Mapping in P_VITEMS:
14#define VI_PATH 0 // Pfad zur BP
15#define VI_REFRESH 1 // Refresheinstellung, s. AddItem
16#define VI_OBJECT 2 // Aktuelles Objekt des VItem
17#define VI_PROPS 3 // Props fuer das echte Objekt
18#define VI_SHADOW_PROPS 4 // Props fuer den shadow/vitem_proxy
19#define VI_LAST_OBJ 5 // letztes mitgenommenes Objekt
20#define VI_LENGTH 6
21
22// VI_REFRESH hat aehnliche Bedeutung wie bei AddItem, mit einer Besonderheit:
23// intern wird in Form eines negativen Wertes kodiert, ob das VItem gerade neu
24// erzeugt werden darf (negativ: ja, positiv: nein).
25
26/* Mehr zu VI_REFRESH:
27 * vitems raeumen sich ggf. selber weg. D.h. wenn der Clone des vitems nicht
28 * mehr ist, heisst das nicht, dass es jemand mitgenommen hat. In dem Fall
29 * muss es sofort (wenn seine Praesenz abgefragt wird) neu erzeugt werden.
30 * Die Logik ist wie folgt:
31 * Der Standardzustand von VI_REFRESH ist ein negierter Wert. Solange dies der
32 * Fall ist, wird ein vitem *immer* erzeugt, sofern es abgefragt wird, aber
33 * nicht da ist.
34 * Wird ein vitem hingegen bewegt/mitgenommen, wird VI_REFRESH auf den
35 * positiven Wert geaendert. In diesem Fall wird es NICHT mehr neu erzeugt,
36 * wenn es nicht als vitem praesent ist. Erst im naechsten reset() wird
37 * geprueft, ob es in Zukunft wieder neu erzeugt werden darf. Wenn ja, wird
38 * der Zahlenwert wieder negiert.
39 * Es ist Absicht, dass VI_REFRESH_ALWAYS auch nur maximal einmal pro reset()
40 * *mitgenommen* werden darf.
41 */
42
43
44//protected void create()
45//{
46//}
47
Zesstra7390fa92019-11-30 13:36:52 +010048protected void create_super() {
49 set_next_reset(-1);
50}
51
Zesstra44030452018-11-12 22:34:02 +010052public varargs void RemoveVItem(string key)
53{
54 mixed vitems=QueryProp(P_VITEMS);
55 if (mappingp(vitems) && member(vitems, key))
56 {
57 // Es wird auch zerstoert, wenn das genommene Objekt gerade im Raum
58 // rumliegt (weil Spieler es hat fallen lassen etc.)
59 if (vitems[key, VI_OBJECT])
60 vitems[key, VI_OBJECT]->remove(1);
61 if (vitems[key, VI_LAST_OBJ]
62 && environment(vitems[key, VI_LAST_OBJ]) == this_object())
63 vitems[key, VI_LAST_OBJ]->remove(1);
64
65 m_delete(vitems, key);
66 SetProp(P_VITEMS, vitems);
67 }
68}
69
70// TODO: braucht es dynamische refresh, shadowprops und props?
71public varargs void AddVItem(string key, int refresh, mapping shadowprops,
72 string path, mapping props)
73{
74 if (!sizeof(key))
75 return;
76 // Wenn path gegeben, muss es eine ladbare Blueprint sind
77 if (sizeof(path) && !load_object(path))
78 return;
79 if (!sizeof(path))
80 {
81 if (mappingp(props))
82 raise_error("Reine vitems erlauben keine <props>\n");
83 }
84
85 refresh ||= VI_REFRESH_NONE;
86 shadowprops ||= ([]);
87 // Wenn reines vItem und keine IDs gesetzt, wird <key> als ID verwendet,
88 // irgendwie muss es ja ansprechbar sein. (Wenn es ein Objekt mit Templat
89 // ist, hat es normalerweise die IDs aus dem Templat. Wenn man das nicht
90 // will, muss man es mit gezielter Angabe von P_IDS in den Shadowprops
91 // ueberschreiben.) Gleiches fuer P_NAME (ohne ist ein "Ding") und P_SHORT
92 // (ohne P_SHORT ist es unsichtbar)
93 if (!path)
94 {
95 if (!member(shadowprops, P_IDS))
96 shadowprops[P_IDS] = ({key});
97 if (!member(shadowprops, P_NAME))
98 shadowprops[P_NAME] = capitalize(key);
99 if (!member(shadowprops, P_SHORT))
100 shadowprops[P_SHORT] = capitalize(key);
101 }
102 mixed vitems=QueryProp(P_VITEMS);
103 if (!mappingp(vitems))
104 vitems = m_allocate(1, VI_LENGTH);
105 vitems[key, VI_PATH] = path;
106 vitems[key, VI_REFRESH] = negate(refresh);
107 vitems[key, VI_PROPS] = props;
108 vitems[key, VI_SHADOW_PROPS] = shadowprops;
109 SetProp(P_VITEMS, vitems);
110}
111
112
113private void configure_object(object ob, mapping props)
114{
115 foreach (string k, mixed v : props)
116 {
117 int reset_prop;
118 if (k[0] == VI_RESET_PREFIX)
119 {
120 reset_prop=1;
121 k=k[1..];
122 }
123 switch(k)
124 {
125 case P_READ_DETAILS:
126 if (reset_prop) ob->RemoveReadDetail(0);
127 walk_mapping(v, "AddReadDetail", ob);
128 break;
129 case P_DETAILS:
130 if (reset_prop) ob->RemoveDetail(0);
131 walk_mapping(v, "AddDetail", ob);
132 break;
133 case P_SMELLS:
134 if (reset_prop) ob->RemoveSmells(0);
135 walk_mapping(v, "AddSmells", ob);
136 break;
137 case P_SOUNDS:
138 if (reset_prop) ob->RemoveSounds(0);
139 walk_mapping(v, "AddSounds", ob);
140 break;
141 case P_TOUCH_DETAILS:
142 if (reset_prop) ob->RemoveTouchDetail(0);
143 walk_mapping(v, "AddTouchDetail", ob);
144 break;
145 case P_IDS:
146 if (reset_prop) ob->SetProp(P_IDS, v);
147 else ob->AddId(v);
148 case P_CLASS:
149 if (reset_prop) ob->SetProp(P_CLASS, v);
150 else ob->AddClass(v);
151 case P_ADJECTIVES:
152 if (reset_prop) ob->SetProp(P_ADJECTIVES, v);
153 else ob->AddAdjective(v);
154 break;
155
156 // Alle anderen Properties stumpf setzen.
157 default:
158 ob->SetProp(k, v);
159 }
160 }
161}
162
163// Clont ein vitem, falls noetig.
164// Nebeneffekt ist aber in jedem Fall auch, dass ein nicht mehr virtuell
165// anwesendes Objekt als VI_LAST_OBJ gespeichert und VI_OBJECT geloescht wird.
166private void check_vitem(string key, string path, int refresh,
167 object obj, mapping props, mapping shadow_props,
168 object last_obj)
169{
170 // Ist es noch da? Ein vItem ist "da", wenn obj auf ein gueltiges Objekt
171 // zeigt, welches *KEIN* Environment hat. (Hat es ein Environment, wurde es
172 // mitgenommen.)
173 if (obj)
174 {
175 if (!environment(obj))
176 return;
177 // wenn es mitgenommen wurde, ist obj in jedem Fall kein vItem mehr:
178 // (eigentlich wird das in VItemMoved schon gemacht, aber mal fuer den
179 // Fall, dass jemand den Shadow hart entsorgt hat o.ae.)
180 last_obj = obj;
181 obj = 0;
182 // und wird ggf. unten neu erzeugt.
183 }
184
185 if (refresh < 0)
186 {
187 object sh;
188 if (path)
189 {
190 obj=clone_object(path);
191 obj->SetAutoObject(1);
192 if (mappingp(props))
193 configure_object(obj, props);
194 // Schatten erzeugen, welcher die Beschreibung des Objekts im Container nach
195 // den Props in shadow_props abaendert.
196 sh = clone_object("/obj/vitem_shadow");
197 sh->set_shadow(obj, shadow_props);
198 }
199 else
200 {
201 obj=clone_object("/obj/vitem_proxy");
202 configure_object(obj, shadow_props);
203 // no shadow needed in this case.
204 }
205 }
206}
207
208// Erzeugt Instanzen der vItems (sofern noetig und erlaubt durch
209// Refresh-Parameter).
210private mixed CheckVItems()
211{
212 mixed vitems=QueryProp(P_VITEMS);
213 if (!mappingp(vitems))
214 vitems = m_allocate(0, VI_LENGTH);
215 walk_mapping(vitems, #'check_vitem);
216 return vitems;
217}
218
219// Liefert alle in diesem Raum virtuell anwesenden Items
220public object *GetVItemClones()
221{
222 mapping vitems = CheckVItems();
223 return filter(m_values(vitems, VI_OBJECT), #'objectp);
224}
225
226public object present_vitem(string complex_desc)
227{
228 foreach(object o : GetVItemClones())
229 {
230 if (o->id(complex_desc))
231 return o;
232 }
233 return 0;
234}
235
236// wird aus dem Shadow fuer das VItem gerufen, wenn es genomment etc. wird.
237// In dem Fall wird das Refresh "gesperrt", d.h. es wird fruehestens nach dem
238// naechsten Reset wieder neu erzeugt - sofern im naechsten Reset die
239// Vorraussetzungen erfuellt sind.
240public void VItemMoved(object ob)
241{
242 if (load_name(previous_object()) == "/obj/vitem_shadow")
243 {
244 mapping vitems = QueryProp(P_VITEMS);
245 if (!mappingp(vitems))
246 return;
247 // passendes vitem suchen
248 foreach(string key, string path, int refresh, object o, mapping props,
249 mapping shadow_props, object last_obj: &vitems)
250 {
251 if (ob != o)
252 continue;
253 else
254 {
255 // mitgenommenes Objekt merken und vitem-objekt loeschen
256 last_obj = o;
257 o = 0;
258 // Sperren gegen sofortiges Neuerzeugen, wenn refresh nicht
259 // VI_REFRESH_INSTANT ist.
260 if (refresh != VI_REFRESH_INSTANT)
261 refresh = negate(refresh);
262 break;
263 }
264 }
265 }
266}
267
268void reset()
269{
270 mapping vitems=QueryProp(P_VITEMS);
271 if (!mappingp(vitems))
272 return;
273 foreach(string key, string path, int refresh, object obj,
274 mapping props, mapping shadow_props, object last_obj : &vitems)
275 {
276 // Wenn negativ (d.h. vItem wurde noch nicht mitgenommen), 0 oder
277 // REFRESH_NONE, muss hier nix gemacht werden.
278 if (refresh <= REFRESH_NONE)
279 continue;
280
281 // Wenn last_obj nicht mehr existiert, darf (ausgenommen natuerlich
282 // REFFRESH_NONE) immer neu erzeugt werden.
283 if (!last_obj)
284 {
285 refresh=negate(refresh);
286 continue;
287 }
288
289 // restliche Faelle
290 switch(refresh)
291 {
292 case VI_REFRESH_MOVE_HOME:
293 // Wenn das Objekt nicht mehr als vItem hier ist (auch wenn es hier im
294 // Raum liegt!), wird es heim bewegt (sprich: zerstoert und neues
295 // vitem).
296 // (Da man hier nur hinkommt, wenn es mitgenommen wurde (refresh > 0),
297 // wird es immer refresht...
298 // Zu beachten: es soll auch nicht hier in diesem Container rumliegen
299 // nach dem Heimbewegen, also zerstoeren!
300 last_obj->remove(1);
301 // Fallthrough
302 case VI_REFRESH_REMOVE:
303 // wenn nicht mehr als vItem hier ist (d.h. auch wenn es hier im Raum
304 // rumliegt!) darf es neu erzeugt werden.
305 // (Hierher kommt die Ausfuehrung nur her, wenn es mitgenommen
306 // wurde, d.h. letztendlich: immer. d.h. Fallthrough.)
307 case VI_REFRESH_ALWAYS:
308 // neu erzeugen
309 refresh=negate(refresh);
310 break;
311 }
312 }
313}
314