blob: 1095b85f31e11d149333972592208ab822ac0e76 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// container/restrictions.c -- container restrictions
4//
5// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
6
7// This is a simple container to put objects in. It defines all functions
8// which are necessary to describe an object which can be filled with
9// other things.
10//
11// It will support restrictions for volume, weight etc.
12//
13// The following properties are defined:
14// P_MAX_WEIGHT - maximum weight which container can carry
15// P_TOTAL_WEIGHT - total weight, with contents.
16// P_WEIGHT - the weight of the container without contents
17//
18// Functions for manipulation of weight
19// MayAddWeight(weight) - Can <weight> be inserted?
20// AddWeight(weight) - Add an amount of <weight>
21//
22// IMPORTANT: unit equals 1 gram
23
24#pragma strict_types
25#pragma save_types
26//#pragma range_check
27#pragma no_clone
28#pragma pedantic
29
30#define NEED_PROTOTYPES
31
32#include "/sys/thing/properties.h"
33#include <properties.h>
34#include <defines.h>
35#include <thing/description.h>
36
37// local properties prototypes
38static int _query_total_weight();
39
40
41private nosave int LastWeightCalc, contents, LastObjectCount, objcount;
42private nosave int last_content_change;
43
44
Zesstra52d40932018-11-28 22:05:15 +010045protected void create()
MG Mud User88f12472016-06-24 23:31:02 +020046{
47 Set( P_WEIGHT_PERCENT, 50 );
48 Set( P_TOTAL_WEIGHT, NOSETMETHOD, F_SET_METHOD );
49 Set( P_TOTAL_WEIGHT, PROTECTED, F_MODE );
50 Set( P_MAX_OBJECTS, 100 );
51 Set( P_TOTAL_OBJECTS, NOSETMETHOD, F_SET_METHOD );
52 Set( P_TOTAL_OBJECTS, PROTECTED, F_MODE );
53 LastWeightCalc = -1;
54 LastObjectCount = -1;
55}
56
57
58static int _query_last_content_change()
59{
60 return last_content_change;
61}
62
63
64// absichtlich public, da es von der simul_efun.c *direkt* aufgerufen
65// wird aus Performancegruenden!
66public varargs int _set_last_content_change( mixed wert )
67{
68 // Der uebergebene Wert ist unerheblich. Die Property
69 // P_LAST_CONTENT_CHANGE wird so oder so bei jedem SetProp()-
70 // Aufruf um eins erhoeht, um der 2s-"Unschaerfe" von time()
71 // aus dem Weg zu gehen.
72 return ++last_content_change;
73}
74
75
76int query_weight_contents()
77{
78 object *objs;
79 int w, w2, i;
80
81 if ( last_content_change == LastWeightCalc )
82 return contents;
83
84 w = 0;
85 objs = all_inventory(this_object());
86 i = sizeof(objs);
87
88 while ( i-- ){
89 if ( !(w2 = (int)objs[i]->QueryProp(P_TOTAL_WEIGHT)) )
90 w2 = (int)objs[i]->QueryProp(P_WEIGHT);
91 w += w2;
92 }
93
94 LastWeightCalc = last_content_change;
95 return contents = w;
96}
97
98
99static int _query_total_objects()
100{
101 if ( last_content_change == LastObjectCount )
102 return objcount;
103
104 objcount = sizeof( filter_objects( all_inventory(), "short" ) );
105 LastObjectCount = last_content_change;
106 return objcount;
107}
108
109
110// diese Funktion sollte von Raeumen natuerlich ueberschrieben werden...
Zesstra52d40932018-11-28 22:05:15 +0100111public int MayAddObject( object ob )
MG Mud User88f12472016-06-24 23:31:02 +0200112{
113 if (ob) {
114 if ( !ob->short() )
115 return 1; // invis-Objekte duerfen immer
116
117 if ( ob == ME || environment(ob) == ME)
118 return 1; // objekt ist schon drin
119 }
120
121 return (QueryProp(P_TOTAL_OBJECTS) < QueryProp(P_MAX_OBJECTS));
122}
123
124
125#define ENV environment
126#define PO previous_object()
127
Zesstra52d40932018-11-28 22:05:15 +0100128public int MayAddWeight( int w )
MG Mud User88f12472016-06-24 23:31:02 +0200129{
130 int nw, aw;
131
132 // was nix wiegt, passt
133 if ( w <= 0 )
134 return 0;
135
136 nw = w; // Gewicht im neuen Container
137 aw = 0; // Gewicht fuer das env()
138
139 // Von Raum in Behaelter im Spieler: Container sieht volles Gewicht (nw=w),
140 // Check im env() (Spieler) mit reduziertem Gewicht.
141 if ( ENV() && PO && ENV(PO) != ENV() )
142 aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1;
143
144 if ( PO && ENV(PO) && ENV(ENV(PO)) ){
145 // Wenn das Objekt ein env() hochbewegt wird, wird das Gewicht im alten
146 // Container abgezogen. Weiterer Check im env() ist unnoetig (aw=0).
147 if ( ENV(ENV(PO)) == ME )
148 nw = w - (int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100;
149 // Eine Ebene tiefer bewegen: Test im neuen Container mit unveraendertem
150 // Gewicht; Check im env() mit um Gewichtsreduktion verringertem Gewicht
151 else if ( present( ME, ENV(PO) ) )
152 aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 - w;
153 // Auf derselben Ebene verschieben (von Paket in Beutel):
154 // Neuer Container sieht volles Gewicht (nw=w), Check im env() mit
155 // Differenz aus Gewicht in Container1 und in Container2.
156 else if ( ENV(ENV(ENV(PO))) && ENV() == ENV(ENV(PO)) )
157 aw = QueryProp(P_WEIGHT_PERCENT) * w / 100
158 - ((int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1);
159 }
160
161 if ( query_weight_contents() + nw > QueryProp(P_MAX_WEIGHT) )
162 // Container kann Gewicht nicht mehr aufnehmen
163 return -1;
164
165 if ( aw && ENV()->MayAddWeight(aw) < 0 )
166 // Umgebung des Containers kann Gewicht nicht mehr aufnehmen
167 return -2;
168
169 return 0;
170}
171
172
173/* Redefine PreventInsert() to prevent inserting of special objects. If
174 * a value greater 0 is returned the object ob can't be inserted in the
175 * container.
176 */
177public int PreventInsert( object ob ) { return 0; }
178public int PreventLeave( object ob, mixed dest ) { return 0; }
179public int PreventInsertLiving( object ob ) { return 0; }
180public int PreventLeaveLiving( object ob, mixed dest ) { return 0; }
181public void NotifyInsert(object ob, object oldenv) { }
Bugfixa5f76da2017-02-16 22:55:33 +0100182public void NotifyLeave(object ob, object dest) { }
MG Mud User88f12472016-06-24 23:31:02 +0200183
184// **** local property methods
185static int _query_total_weight()
186{
187 return QueryProp(P_WEIGHT) +
188 (QueryProp(P_WEIGHT_PERCENT) * query_weight_contents() / 100);
189}
190
191
192// Hilfsfunktion
193static int _behalten( object ob, string uid )
194{
195 return (string)ob->QueryProp(P_KEEP_ON_SELL) == uid;
196}
197
198
199/*
200 *
201 * get a list of all contained objects matching a complex description
202 *
203 */
204
205#define POS_INVERS 0x01 /* nur zur funktionsinternen Verwendung */
206#define POS_LETZTES 0x02 /* nur zur funktionsinternen Verwendung */
207
Zesstra52d40932018-11-28 22:05:15 +0100208public object *present_objects( string complex_desc )
MG Mud User88f12472016-06-24 23:31:02 +0200209{
210 int i; // Zaehlervariable
211 int meth; // 0x01 = invers?, 0x02 = letztes?
212 object ob; // einzelnes temporaeres Objekt
213 object *obs; // liste der ausgewaehlten Objekte
214 object *erg; // zum sammeln bis es nachher zurueckgegeben wird...
215 string *strlst; // liste aller Gruppen die mit AND verknuepft sind
216 object haufen;
217
218 strlst = allocate(2);
219
220 if ( sscanf( complex_desc, "%s ausser %s", strlst[0], strlst[1]) == 2 ){
221 erg = present_objects( strlst[0] );
222 obs = present_objects( strlst[1] );
223 return erg-obs;
224 }
225
226 strlst = explode( complex_desc, " und " );
227 erg = ({});
228
229 for ( i = sizeof(strlst); i--; )
230 {
231 complex_desc = strlst[i];
232 // auf letzte/letztes/letzten pruefen...
233 if ( complex_desc[0..5] == "letzte" ){
234 switch( complex_desc[6..6] ){
235 case " ":
236 meth |= POS_LETZTES;
237 complex_desc = complex_desc[7..];
238 break;
239 case "s":
240 case "n":
241 case "m":
242 case "r":
243 if ( complex_desc[7..7] != " " )
244 break;
245 meth |= POS_LETZTES;
246 complex_desc = complex_desc[8..];
247 break;
248 default:
249 }
250 }
251
252 // auf verneinung pruefen
253 if ( complex_desc[0..5] == "nicht " ){
254 meth |= POS_INVERS;
255 complex_desc = complex_desc[6..];
256 }
257
258 obs=({});
259 // nun nach Main-Ids (Gruppen) suchen...
260 if ( meth & POS_LETZTES )
261 { // geht es nur um den letzten Gegenstand?
262 switch( complex_desc ){
263 case "waffe":
264 obs = filter_objects( all_inventory(), "QueryProp",
265 P_WEAPON_TYPE );
266 break;
267
268 case "ruestung":
269 obs = filter_objects( all_inventory(), "QueryProp",
270 P_ARMOUR_TYPE );
271 break;
272
273 case "kleidung":
274 obs = filter_objects( all_inventory(), "IsClothing");
275 break;
276
277 case "verschiedenem":
278 case "verschiedenes":
279 obs = all_inventory();
280 obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
281 obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
282 obs -= filter_objects( obs, "IsClothing");
283 obs -= filter( obs, #'living/*'*/ );
284 break;
285
286 case "eigenes":
287 case "meins":
288 if (objectp(haufen=present("\nhaufen "+
289 this_player()->name(WEM)))) {
290 obs = all_inventory(haufen);
291 }
292 // kein break;, Fall-through!
293 case "behaltenem":
294 case "behaltenes":
295 obs += filter( all_inventory(), "_behalten", ME,
296 getuid(this_player() || previous_object()) );
297
298 obs += (QueryProp(P_ARMOURS) || ({}))
299 + ({ QueryProp(P_WEAPON) }) - ({ 0 });
300 break;
301
302 case "gegenstand":
303 obs = all_inventory() -
304 filter( all_inventory(), #'living/*'*/ );
305 break;
306
307 default:
308 obs = filter_objects( all_inventory(), "id", complex_desc );
309 }
310
311 // unsichtbare objekte entfernen
312 obs = filter_objects( obs, "short" );
313
314 // letzten Gegenstand raussuchen
315 obs = obs[0..0];
316 } // if (letzter Gegenstand)
317 else
318 { // ganze Gruppen und nicht nur das letzte Objekt
319 switch ( complex_desc )
320 {
321 case "allem":
322 case "alles":
323 case "jeden gegenstand":
324 case "jedem gegenstand":
325 case "gegenstaende":
326 case "alle gegenstaende":
327 case "allen gegenstaenden":
328 if ( meth & POS_INVERS )
329 continue; // alles nicht = nichts :)
330
331 obs = all_inventory() -
332 filter( all_inventory(), #'living/*'*/ );
333 break;
334
335 case "waffen":
336 case "jede waffe":
337 case "jeder waffe":
338 case "alle waffen":
339 case "allen waffen":
340 obs = filter_objects( all_inventory(), "QueryProp",
341 P_WEAPON_TYPE );
342 break;
343
344 case "ruestungen":
345 case "jede ruestung":
346 case "jeder ruestung":
347 case "alle ruestungen":
348 case "allen ruestungen":
349 obs = filter_objects( all_inventory(), "QueryProp",
350 P_ARMOUR_TYPE );
351 break;
352
353 case "kleidung":
354 case "jede kleidung":
355 case "jeder kleidung":
356 case "alle kleidung":
357 case "allen kleidung":
358 obs = filter_objects( all_inventory(), "IsClothing");
359 break;
360
361 case "gegenstand":
362 obs = filter_objects( all_inventory() -
363 filter( all_inventory(),
364 #'living/*'*/ ),
365 "short" )[0..0];
366 break;
367
368 case "verschiedenem":
369 case "verschiedenes":
370 obs = all_inventory();
371 obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
372 obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
373 obs -= filter_objects( obs, "IsClothing");
374 obs -= filter( obs, #'living/*'*/ );
375 break;
376
377 case "eigenes":
378 case "eigenem":
379 case "eigenen":
380 case "meins":
381 case "alles eigene":
382 if (objectp(haufen=present("\nhaufen "+
383 this_player()->name(WEM)))) {
384 obs = all_inventory(haufen);
385 }
386 // kein break;, Fall-through!
387 case "behaltenem":
388 case "behaltenen":
389 case "behaltenes":
390 case "alles behaltene":
391 obs += filter( all_inventory(), "_behalten", ME,
392 getuid(this_player() || previous_object()) );
393
394 obs += (QueryProp(P_ARMOURS) || ({}))
395 + ({ QueryProp(P_WEAPON) }) - ({ 0 });
396 break;
397
398 default:
399 if ( complex_desc[0..3] == "jede" ||
400 complex_desc[0..4] == "alle ")
401 {
402 if ( complex_desc[4..4] == " " )
403 {
404 obs = filter_objects( all_inventory(), "id",
405 complex_desc[5..] );
406 break;
407 }
408 else
409 {
410 switch( complex_desc[4..5] )
411 {
412 case "m ":
413 case "r ":
414 case "n ":
415 case "s ":
416 obs = filter_objects( all_inventory(), "id",
417 complex_desc[6..] );
418 break;
419
420 default:
421 obs = 0;
422 }
423 if (obs)
424 break;
425 }
426 }
427
428 // Der Normalfall: einzelne ID...
429 ob = present( complex_desc, ME );
430 // Achtung: dieser Teil setzt das for() fort (continue) und
431 // umgeht dabei die Pruefung auf Sichtbarkeit nach dem Ende vom
432 // switch(). Aus diesem Grunde muss hier selber geprueft
433 // werden.
434 if ( meth & POS_INVERS )
435 {
436 if ( ob && ob != ME )
437 erg += (filter_objects( all_inventory(), "short" )
438 - ({ ob }) );
439 else
440 erg += filter_objects( all_inventory(), "short" );
441 }
442 else if ( ob && ob != ME && !ob->QueryProp(P_INVIS) )
443 erg += ({ ob }); //Normalfall: einzelne ID
444
445 continue;
446
447 } // switch
448 // unsichtbare objekte entfernen
449 obs = filter_objects( obs, "short" );
450 } // else
451
452 if ( meth & POS_INVERS )
453 erg += ( filter_objects( all_inventory(), "short" ) - obs );
454 else
455 erg += obs;
456 } // for
457 return erg;
458}
459
460/*
461 * returns a list of all found objects inside itself
462 * may call same function in objects inside
463 *
464 * Funktion wird nicht mehr von put_and_get aufgerufen, stattdessen wird
465 * direkt present_objects benutzt!
466 */
Zesstra52d40932018-11-28 22:05:15 +0100467public object *locate_objects( string complex_desc, int info ) {
MG Mud User88f12472016-06-24 23:31:02 +0200468 string was, wo;
469
470 if ( sscanf( complex_desc, "%s in %s", was, wo ) == 2 ){
471 object *found_obs = ({});
472 foreach(object invob: present_objects(wo)) {
473 // || ({}) weil invob ein Objekt ohne locate_objects() sein koennte.
474 found_obs += (object *)invob->locate_objects( was, info) || ({});
475 }
476 return found_obs;
477 }
478 // kein "in" gefunden
479 return present_objects( complex_desc );
480}
481