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