blob: 64d98991c52fed9351dfdbca738fb83fb6e30de8 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2/** @file
3* Implementation fuer Sockelitems.
4* Langbeschreibung... TODO
5* @author Zesstra + Arathorn
6* @date xx.05.2008
7* @version $Id$
8*/
9
10/* Changelog:
11*/
12#pragma strong_types
13#pragma save_types
14#pragma no_clone
15#pragma no_shadow
16#pragma pedantic
17#pragma range_check
18
19#define NEED_PROTOTYPES
20#include <thing/sockets.h>
21#undef NEED_PROTOTYPES
22#include <defines.h>
23#include <lpctypes.h>
24
25#ifndef DEBUG
26/** Outputs debug message to Maintainer, if Maintainer is logged in.
27*/
28#define DEBUG(x) __debug_msg__(x)
29#endif
30
31
32private void __debug_msg__(string x) {
33 if (find_player("zesstra"))
34 tell_object(find_player("zesstra"),"SDBG: "+x+"\n");
35}
36
37/** Setzt \a item in einen passenden Sockel ein.
38 Wird vom Handwerker gerufen. Sofern die Pruefung mittels TestSocketItem()
39 erfolgreich ist, werden die Props dieses Objekt durch die im Item
40 gespeicherten in P_SOCKET_PROPS ergaenzt, ggf. P_RESTRICTION geaendert und
41 Name der Blueprint, Name und P_SOCKET_PROPS in P_SOCKETS gespeichert, um
42 spaeter nachvollziehen zu koennen, wie dieses Objekt bereits modifiziert
43 wurde.
44 @attention Am Ende der Funktion wird \a item zerstoert und darf nicht mehr
45 benutzt werden!
46 @param[in] item Objekt, welches in einen passenden Socket eingesetzt werden
47 soll.
48 @return 1, falls Einsetzen erfolgreich, <0 wenn nicht.
49 @sa TestSocketItem(object)
50 */
51public int MountSocketItem(object item) {
52 if (!objectp(item)) return SOCKET_NO_OBJECT;
53 if ((int res=TestSocketItem(item)) != SOCKET_OK)
54 return res;
55 mapping idata=(mapping)item->QueryProp(P_SOCKET_PROPS);
56 // TODO: Spezialbehandlung fuer Props, bei denen das Objekt erhalten bleiben
57 // muss. (z.B. P_DEFEND_FUNC).
58
59 // zu modifizierende Props addieren
60 foreach(string propname, mixed pval: idata) {
61 SetProp(propname, AddToEntity(QueryProp(propname), pval) );
62 }
63 // Restrictions hinzufuegen.
64 SetProp(P_RESTRICTIONS,
65 AddToEntity(QueryProp(P_RESTRICTIONS),
66 item->QueryProp(P_SOCKET_RESTR_USE)) );
67
68 // Daten ueber dieses Socketitem abspeichern.
69 mixed sockets=QueryProp(P_SOCKETS)[item->QueryProp(P_SOCKET_TYPE)];
70 // freier Sockel muss existieren (->TestSocketItem())
71 int index=member(sockets, SOCKET_FREE);
72 sockets[index] = idata + (["BLUE_NAME": load_name(item),
73 "DESCRIPTION": item->name(WER) ]);
74
75 // ggf. Beschreibung aktualisieren?
76 // ggf. Sonderbehandlung fuer Props, bei denen das Objekt noch gebraucht
77 // wird (z.B. P_DEFEND_INFO)
78
79 // Prop schuetzen, sobald Daten drin stehen.
80 Set(P_SOCKETS, PROTECTED|NOSETMETHOD, F_MODE_AS);
81
82 // item entsorgen
83 if (!item->remove(1))
84 raise_error(sprintf("MountSocketItem() in %O: %O hat Ausfuehrung von "
85 "remove() verweigert.\n", ME, item));
86 return SOCKET_OK;
87}
88
89/** Prueft, ob \a item in einen Sockel eingesetzt werden kann.
90 Prueft, ob es fuer das Item einen passenden Sockel gibt und ob
91 es die formalen Anforderungen erfuellt, dort eingesetzt zu werden.
92 Kann vom Handwerker vor dem echten Einsetzen gerufen werden.
93 Wird auch von MountSocketItem(object) gerufen.
94 @param[in] item Objekt, welches auf Eignung zum Einsetzen in einen Sockel
95 getestet werden soll.
96 @return 1, falls ein passender und freier Sockel existiert, 0 sonst.
97 @sa MountSocket(object)
98*/
99public int TestSocketItem(object item) {
100
101 if (!objectp(item)) return SOCKET_NO_OBJECT;
102
103 // ist es ein Sockelitem und hat es einen gueltigen Typ?
104 mapping idata = (mapping)item->QueryProp(P_SOCKET_PROPS);
105 if (!mappingp(idata) || !sizeof(idata))
106 return SOCKET_NO_DATA;
107 string styp=(string)item->QueryProp(P_SOCKET_TYPE);
108 if (!stringp(styp)
109 || member(VALID_SOCKET_TYPES, styp) == -1)
110 return SOCKET_INVALID_TYPE;
111
112 // Hat dieses Item ueberhaupt Sockel? Und wenn ja, haben wir nen freien
113 // Socke fuer den betreffenden Typ?
114 mapping mysockets = QueryProp(P_SOCKETS);
115 if (!mappingp(mysockets) || !sizeof(mysockets))
116 return SOCKET_NO_SOCKETS;
117 if (!member(mysockets, styp)
118 || !member(mysockets[styp], SOCKET_FREE) == -1 )
119 return SOCKET_NONE_AVAILABLE;
120
121 // Handwerker pruefen.
122 // TODO: Soll die Fehlermeldung komplett vom Aufrufer erledigt werden oder
123 // soll es einen Heinweis geben, warum der Handwerker nicht geeignet ist?
124 object craftsman = previous_object();
125 mapping restr = (mapping)item->QueryProp(P_SOCKET_RESTR_MOUNT);
126 if (!objectp(craftsman)
127 || (mappingp(restr)
128 && "/std/restriction_checker"->check_restrictions(craftsman,
129 restr)))
130 return SOCKET_NO_EXPERTISE;
131
132 // da P_RESTRICTION nur beim Anziehen/Zuecken geprueft wird, darf man ein
133 // Item nicht in getragenem Zustand modifizieren.
134 if (objectp((object)item->QueryProp(P_WIELDED))
135 || objectp((object)item->QueryProp(P_WORN)))
136 return SOCKET_ITEM_INUSE;
137
138 return SOCKET_OK;
139}
140
141/** Liefert Infos ueber die Sockets (Typ, was drin ist, etc.\ ).
142 Primaer fuer Gilden gedacht.
143*/
144public mixed GetSocketInfo() {
145}
146
147/* **************************** private ************************* */
148
149/** Addiert zwei Variablen, sofern sie kompatibel sind.
150 Lassen sich die beiden Datentypen nicht sinnvoll addieren, erfolgt keine
151 Addition und \a old wird zurueck geliefert (z.B. bei Objekten, Closures).
152 Hierbei erfolgt z.B. bei Mappings eine wertweise Addition, keine blosse
153 Ersetzung der Keys wie bei der normalen Mapping-Addition.
154 @param[in,out] old Datum, zu dem addiert werden soll. Wird bei Mappings
155 geaendert.
156 @param[in] new Datum, welches addierten soll.
157 @return Summe von old und new, falls die Datentypen kompatibel sind. Falls
158 nicht, kann old zurueckgegeben werden. Der Datentyp von
159 @attention \b Rekursiv! Kann teuer werden.\n
160 Kann (modifizierte) \a old oder \a new oder ganz neues Datum
161 zurueckliefern, d.h. der zurueckgelieferte Datentyp muss nicht dem
162 Datentyp von old entsprechen. Ebenso erzeugt z.B. die Addition zweier
163 Arrays ein neues Array.
164 Wenn die korrespondierenden Werte nicht den gleichen Datentyp haben,
165 findet u.U. keine Addition statt oder es wird eine Datentyp-Umwandlung
166 durchgefuehrt, z.B. Int + Float == Float.\n
167 */
168private mixed AddToEntity(mixed old, mixed new) {
169
170 // einfachste Faelle:
171 if (!old)
172 return new;
173 else if (!new)
174 return old;
175
176 // Typ bestimmen
177 int oldtype = typeof(old);
178 int newtype = typeof(new);
179 // Variablen gleichen Typs sind einfach.
180 if (oldtype == newtype) {
181 switch (oldtype) {
182 // einige Typen werden stumpf addiert.
183 case T_NUMBER:
184 case T_STRING:
185 case T_FLOAT:
186 case T_POINTER: // TODO: anderes Verhalten?
187 return old+new;
188 // Mappings werden wertweise addiert.
189 case T_MAPPING:
190 // nur wenn die Breite des 2. Summanden <= der des 1. Summanden ist,
191 // lassen sich Mappings hiermit addieren.
192 if (widthof(new) > widthof(old))
193 return old;
194 // new auf old addieren. Keys werden nicht ersetzt, sondern nach
195 // Moeglichkeit die werte unter den keys jeweils addiert.
196 // map() nur zum Uebergeben aller Keys+Value. AddToMapping aendert
197 // direkt das uebergebene Mapping old.
198 map(new, #'AddToMapping, old);
199 // alles hier nicht aufgefuehrte kann nicht addiert werden.
200 default: return old;
201 }
202 }
203 // Ints und Floats sind auch noch gut addierbar.
204 else if ((oldtype == T_FLOAT && newtype == T_NUMBER) ||
205 (oldtype == T_NUMBER && newtype == T_FLOAT) )
206 return old + new;
207 // Arrays lassen sich auch gut verwursten (new kann kein Array sein).
208 // Umgekehrter Fall waere auch meoglich, aber der Datentyp von old waere
209 // sehr deutlich unterschiedlich vom urspruenglichen.
210 else if (oldtype == T_POINTER)
211 return old+({new});
212 // Strings und Zeichenliterale (Ints) sind Ansichtssache.
213 else if (oldtype == T_STRING && newtype == T_NUMBER)
214 return sprintf("%s%c",old,new);
215 else if (oldtype == T_NUMBER && newtype == T_STRING)
216 return sprintf("%c%s",old,new);
217
218 // Fall-through
219 return old;
220}
221
222/** Addiert einen Schluessel und seine Werte zu einem Mapping, ggf.\ auf die
223 * bestehenden Werte des Mappings \a old.
224 * Der Key und seine Werte ersetzen bestehende Werte in \a old nicht, wie es
225 * die normale Mapping-Addition des Driver macht. Stattdessen wird versucht,
226 * die neuen Werte auf die entsprechenden alten Werte zu addieren. Falls die
227 * Datentypen zweier Werte inkompatibel sind, erfolgt keine Addition und der
228 * alte Werte hat Bestand.
229 @param[in] key (mixed) Wert, der in das Mapping addiert wird.
230 @param[in] values (mixed)
231 @param[in,out] old Mapping, in das addiert wird.
232 @attention Die Breites des 2. Summanden darf \b nicht groesser sein als die
233 Breite des 1, Summanden! \n
234 Die korrespondieren Werte sollten den gleichen Datentyp haben, sonst
235 findet u.U. keine Addition statt oder es wird eine Datentyp-Umwandlung
236 durchgefuehrt, z.B. Int + Float == Float.\n
237 */
238private void AddToMapping(mixed key, mixed values, mapping old) {
239 if (!mappingp(old)) return;
240 if (!pointerp(values)) values = ({values});
241
242 // wenn der Key noch nicht existiert, ists einfach.
243 if (!member(old, key))
244 m_add(old, key, values...); // flatten operator ... cool. ;-)
245 else {
246 // sonst muessen wir teure handarbeit machen. Insb. weil die Values einen
247 // beliebigen Datentyp haben koennen, u.a. Mappings, weswegen wir wieder
248 // rekursiv AddToEntity() rufen muessen.
249 for(int i=sizeof(values) ; i-- ; ) {
250 old[key,i] = AddToEntity(old[key,i], values[i]);
251 }
252 }
253}
254