blob: acd08b2d9fa4bb3b7d5ab75e7e5f681b8aa9ac8c [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
48public varargs void RemoveVItem(string key)
49{
50 mixed vitems=QueryProp(P_VITEMS);
51 if (mappingp(vitems) && member(vitems, key))
52 {
53 // Es wird auch zerstoert, wenn das genommene Objekt gerade im Raum
54 // rumliegt (weil Spieler es hat fallen lassen etc.)
55 if (vitems[key, VI_OBJECT])
56 vitems[key, VI_OBJECT]->remove(1);
57 if (vitems[key, VI_LAST_OBJ]
58 && environment(vitems[key, VI_LAST_OBJ]) == this_object())
59 vitems[key, VI_LAST_OBJ]->remove(1);
60
61 m_delete(vitems, key);
62 SetProp(P_VITEMS, vitems);
63 }
64}
65
66// TODO: braucht es dynamische refresh, shadowprops und props?
67public varargs void AddVItem(string key, int refresh, mapping shadowprops,
68 string path, mapping props)
69{
70 if (!sizeof(key))
71 return;
72 // Wenn path gegeben, muss es eine ladbare Blueprint sind
73 if (sizeof(path) && !load_object(path))
74 return;
75 if (!sizeof(path))
76 {
77 if (mappingp(props))
78 raise_error("Reine vitems erlauben keine <props>\n");
79 }
80
81 refresh ||= VI_REFRESH_NONE;
82 shadowprops ||= ([]);
83 // Wenn reines vItem und keine IDs gesetzt, wird <key> als ID verwendet,
84 // irgendwie muss es ja ansprechbar sein. (Wenn es ein Objekt mit Templat
85 // ist, hat es normalerweise die IDs aus dem Templat. Wenn man das nicht
86 // will, muss man es mit gezielter Angabe von P_IDS in den Shadowprops
87 // ueberschreiben.) Gleiches fuer P_NAME (ohne ist ein "Ding") und P_SHORT
88 // (ohne P_SHORT ist es unsichtbar)
89 if (!path)
90 {
91 if (!member(shadowprops, P_IDS))
92 shadowprops[P_IDS] = ({key});
93 if (!member(shadowprops, P_NAME))
94 shadowprops[P_NAME] = capitalize(key);
95 if (!member(shadowprops, P_SHORT))
96 shadowprops[P_SHORT] = capitalize(key);
97 }
98 mixed vitems=QueryProp(P_VITEMS);
99 if (!mappingp(vitems))
100 vitems = m_allocate(1, VI_LENGTH);
101 vitems[key, VI_PATH] = path;
102 vitems[key, VI_REFRESH] = negate(refresh);
103 vitems[key, VI_PROPS] = props;
104 vitems[key, VI_SHADOW_PROPS] = shadowprops;
105 SetProp(P_VITEMS, vitems);
106}
107
108
109private void configure_object(object ob, mapping props)
110{
111 foreach (string k, mixed v : props)
112 {
113 int reset_prop;
114 if (k[0] == VI_RESET_PREFIX)
115 {
116 reset_prop=1;
117 k=k[1..];
118 }
119 switch(k)
120 {
121 case P_READ_DETAILS:
122 if (reset_prop) ob->RemoveReadDetail(0);
123 walk_mapping(v, "AddReadDetail", ob);
124 break;
125 case P_DETAILS:
126 if (reset_prop) ob->RemoveDetail(0);
127 walk_mapping(v, "AddDetail", ob);
128 break;
129 case P_SMELLS:
130 if (reset_prop) ob->RemoveSmells(0);
131 walk_mapping(v, "AddSmells", ob);
132 break;
133 case P_SOUNDS:
134 if (reset_prop) ob->RemoveSounds(0);
135 walk_mapping(v, "AddSounds", ob);
136 break;
137 case P_TOUCH_DETAILS:
138 if (reset_prop) ob->RemoveTouchDetail(0);
139 walk_mapping(v, "AddTouchDetail", ob);
140 break;
141 case P_IDS:
142 if (reset_prop) ob->SetProp(P_IDS, v);
143 else ob->AddId(v);
144 case P_CLASS:
145 if (reset_prop) ob->SetProp(P_CLASS, v);
146 else ob->AddClass(v);
147 case P_ADJECTIVES:
148 if (reset_prop) ob->SetProp(P_ADJECTIVES, v);
149 else ob->AddAdjective(v);
150 break;
151
152 // Alle anderen Properties stumpf setzen.
153 default:
154 ob->SetProp(k, v);
155 }
156 }
157}
158
159// Clont ein vitem, falls noetig.
160// Nebeneffekt ist aber in jedem Fall auch, dass ein nicht mehr virtuell
161// anwesendes Objekt als VI_LAST_OBJ gespeichert und VI_OBJECT geloescht wird.
162private void check_vitem(string key, string path, int refresh,
163 object obj, mapping props, mapping shadow_props,
164 object last_obj)
165{
166 // Ist es noch da? Ein vItem ist "da", wenn obj auf ein gueltiges Objekt
167 // zeigt, welches *KEIN* Environment hat. (Hat es ein Environment, wurde es
168 // mitgenommen.)
169 if (obj)
170 {
171 if (!environment(obj))
172 return;
173 // wenn es mitgenommen wurde, ist obj in jedem Fall kein vItem mehr:
174 // (eigentlich wird das in VItemMoved schon gemacht, aber mal fuer den
175 // Fall, dass jemand den Shadow hart entsorgt hat o.ae.)
176 last_obj = obj;
177 obj = 0;
178 // und wird ggf. unten neu erzeugt.
179 }
180
181 if (refresh < 0)
182 {
183 object sh;
184 if (path)
185 {
186 obj=clone_object(path);
187 obj->SetAutoObject(1);
188 if (mappingp(props))
189 configure_object(obj, props);
190 // Schatten erzeugen, welcher die Beschreibung des Objekts im Container nach
191 // den Props in shadow_props abaendert.
192 sh = clone_object("/obj/vitem_shadow");
193 sh->set_shadow(obj, shadow_props);
194 }
195 else
196 {
197 obj=clone_object("/obj/vitem_proxy");
198 configure_object(obj, shadow_props);
199 // no shadow needed in this case.
200 }
201 }
202}
203
204// Erzeugt Instanzen der vItems (sofern noetig und erlaubt durch
205// Refresh-Parameter).
206private mixed CheckVItems()
207{
208 mixed vitems=QueryProp(P_VITEMS);
209 if (!mappingp(vitems))
210 vitems = m_allocate(0, VI_LENGTH);
211 walk_mapping(vitems, #'check_vitem);
212 return vitems;
213}
214
215// Liefert alle in diesem Raum virtuell anwesenden Items
216public object *GetVItemClones()
217{
218 mapping vitems = CheckVItems();
219 return filter(m_values(vitems, VI_OBJECT), #'objectp);
220}
221
222public object present_vitem(string complex_desc)
223{
224 foreach(object o : GetVItemClones())
225 {
226 if (o->id(complex_desc))
227 return o;
228 }
229 return 0;
230}
231
232// wird aus dem Shadow fuer das VItem gerufen, wenn es genomment etc. wird.
233// In dem Fall wird das Refresh "gesperrt", d.h. es wird fruehestens nach dem
234// naechsten Reset wieder neu erzeugt - sofern im naechsten Reset die
235// Vorraussetzungen erfuellt sind.
236public void VItemMoved(object ob)
237{
238 if (load_name(previous_object()) == "/obj/vitem_shadow")
239 {
240 mapping vitems = QueryProp(P_VITEMS);
241 if (!mappingp(vitems))
242 return;
243 // passendes vitem suchen
244 foreach(string key, string path, int refresh, object o, mapping props,
245 mapping shadow_props, object last_obj: &vitems)
246 {
247 if (ob != o)
248 continue;
249 else
250 {
251 // mitgenommenes Objekt merken und vitem-objekt loeschen
252 last_obj = o;
253 o = 0;
254 // Sperren gegen sofortiges Neuerzeugen, wenn refresh nicht
255 // VI_REFRESH_INSTANT ist.
256 if (refresh != VI_REFRESH_INSTANT)
257 refresh = negate(refresh);
258 break;
259 }
260 }
261 }
262}
263
264void reset()
265{
266 mapping vitems=QueryProp(P_VITEMS);
267 if (!mappingp(vitems))
268 return;
269 foreach(string key, string path, int refresh, object obj,
270 mapping props, mapping shadow_props, object last_obj : &vitems)
271 {
272 // Wenn negativ (d.h. vItem wurde noch nicht mitgenommen), 0 oder
273 // REFRESH_NONE, muss hier nix gemacht werden.
274 if (refresh <= REFRESH_NONE)
275 continue;
276
277 // Wenn last_obj nicht mehr existiert, darf (ausgenommen natuerlich
278 // REFFRESH_NONE) immer neu erzeugt werden.
279 if (!last_obj)
280 {
281 refresh=negate(refresh);
282 continue;
283 }
284
285 // restliche Faelle
286 switch(refresh)
287 {
288 case VI_REFRESH_MOVE_HOME:
289 // Wenn das Objekt nicht mehr als vItem hier ist (auch wenn es hier im
290 // Raum liegt!), wird es heim bewegt (sprich: zerstoert und neues
291 // vitem).
292 // (Da man hier nur hinkommt, wenn es mitgenommen wurde (refresh > 0),
293 // wird es immer refresht...
294 // Zu beachten: es soll auch nicht hier in diesem Container rumliegen
295 // nach dem Heimbewegen, also zerstoeren!
296 last_obj->remove(1);
297 // Fallthrough
298 case VI_REFRESH_REMOVE:
299 // wenn nicht mehr als vItem hier ist (d.h. auch wenn es hier im Raum
300 // rumliegt!) darf es neu erzeugt werden.
301 // (Hierher kommt die Ausfuehrung nur her, wenn es mitgenommen
302 // wurde, d.h. letztendlich: immer. d.h. Fallthrough.)
303 case VI_REFRESH_ALWAYS:
304 // neu erzeugen
305 refresh=negate(refresh);
306 break;
307 }
308 }
309}
310