blob: 504c72d3af51ceb6db26dd2cbe8d06a92b74d749 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001#pragma strong_types
2#pragma save_types
3#pragma pedantic
4#pragma range_check
5#pragma no_clone
6
7inherit "/std/trading_price";
8
9#define NEED_PROTOTYPES
10#include <thing/commands.h>
11#include <thing/description.h>
12#undef NEED_PROTOTYPES
13
14#include <defines.h>
15#include <properties.h>
16#include <rooms.h>
17#include <language.h>
18#include <moving.h>
19#include <routingd.h>
20#include <bank.h>
21#include <combat.h>
22#include <input_to.h>
23#include <unit.h>
24#include <money.h>
25
26// TODO: pruefen, um die Variablen private sein koennen.
27
28// allgemein benoetige Variablen
29nosave string storage; // Filename des Stores in dem die Objekte lagern...
30nosave mapping ob_anz; // wie oft ist ein Objekt im Laden vorhanden?
31
32// Jetzt Variablen fuer staendig verfuegbare Objekte:
33nosave string *fixed_obj; // Liste der staendig verfuegbaren Objekte
34nosave mapping fixed_value; // Preise bei Sonderangeboten
35nosave mapping fixed_ids; // Ids
36
37varargs void AddFixedObject(string str, int val, string|string* ids)
38{
39 int i;
40
41 // Achtung, bei solchen Objekten muss die Blueprint initialisiert werden!
42 if (!str) return;
43 if (!val) val=str->QueryProp(P_VALUE);
44 if (!ids)
45 {
46 if (str->QueryProp("u_ids")) // units haben keine P_IDS
47 ids=str->QueryProp("u_ids")[0];
48 else
49 ids=str->QueryProp(P_IDS);
50 }
51 if (!pointerp(ids))
52 {
53 if (stringp(ids))
54 ids=({ids});
55 else
56 ids=({});
57 }
58
59 fixed_obj += ({str});
60 fixed_value[str] = val;
61 // Alle IDs entfernen, die Sonderzeichen enthalten. Die koennte ein Spieler
62 // bei "kaufe" ohnehin nicht eingeben.
63 ids -= regexp(ids, "[\b\n\r\t]");
64 foreach(string id : ids)
65 {
66 // Nur IDs aufnehmen, die keine Grossbuchstaben enthalten, da Spieler
67 // diese ebenfalls nicht eingeben koennte.
68 if ( lowerstring(id) == id )
69 fixed_ids[id]=str;
70 }
71}
72
73void RemoveFixedObject(string filename)
74{
75 if( !stringp(filename) || !sizeof(fixed_obj))
76 return;
77 if( member(fixed_obj, filename)==-1 )
78 return;
79
80 fixed_obj -= ({ filename });
81 m_delete(fixed_value, filename);
82
83 foreach(string id, string file : fixed_ids)
84 {
85 if ( file == filename )
86 m_delete(fixed_ids, id);
87 }
88}
89
90static string SetStorageRoom(string str)
91{
92 if (str && stringp(str)) return storage=str;
93 return 0;
94}
95
96string QueryStorageRoom()
97{ return storage; }
98
99protected void create()
100{
101 object router;
102
103 if (object_name(this_object()) == __FILE__[0..<3])
104 {
105 set_next_reset(-1);
106 return;
107 }
108
109 trading_price::create();
110
111 SetProp( P_NAME, "Haendler" );
112 SetProp( P_GENDER, MALE );
113 SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_SHOP );
114
115 AddCmd("zeige","list");
116 AddCmd(({"kauf","kaufe","bestell","bestelle"}),"buy");
117 AddCmd(({"verkauf","verkaufe","verk"}),"sell");
118 AddCmd(({"versetz","versetze"}),"force_sell");
119 AddCmd(({"schaetz","schaetze"}),"evaluate");
120 AddCmd(({"untersuche","unt"}), "show_obj");
121
122 SetTradingData(50000,300,3);
123
124 ob_anz=([]);
125 fixed_obj=({});fixed_value=([]);fixed_ids=([]);
126
127 AddFixedObject(BOERSE, 80,({ "boerse","geldboerse"}));
128
129 if (!clonep(ME) && objectp(router=find_object(ROUTER)))
130 router->RegisterTarget(TARGET_SHOP,object_name(ME));
131}
132
133protected void create_super() {
134 set_next_reset(-1);
135}
136
137// Legacy-Version von GetShopItems() fuer die erbenden Laeden, die auf
138// die Funktion in dieser Form angewiesen sind.
139static mixed *GetList()
140{
141 object store = load_object(storage);
142 store->_register_shop(ME);
143
144 mixed *output=({});
145 if (!objectp(store))
146 return output;
147
148 mixed tmp = map(fixed_obj, #'load_object)+all_inventory(store);
149 mapping tmp2 = ([]);
150 mixed str;
151 string comp;
152 int i;
153 int s=1;
154
155 for (i=sizeof(tmp)-1 ; i>=0 ; i--)
156 {
157 str = ({ ({ sprintf("%-25.25s%7.7d",
158 (tmp[i]->short()||"???")[0..<3],
159 QueryBuyValue(tmp[i], PL)),
160 tmp[i] }) });
161 comp=str[0][0][0..25];
162 if (!tmp2[comp])
163 {
164 tmp2[comp] = s++;
165 output += str;
166 }
167 else output[tmp2[comp]-1][0] = str[0][0];
168 }
169 return output;
170}
171
172// Legacy-Version von PrintList() fuer die erbenden Laeden, die auf
173// die Funktion in dieser Form angewiesen sind.
174static int DoList(string query_fun)
175{
176 mixed* output=GetList();
177 int si = sizeof(output);
178 if (!si)
179 {
180 write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
181 return 1;
182 }
183 string out="";
184 int i;
185 int indent;
186 for (i=0; i<si; i++)
187 {
188 if (call_other(ME, query_fun, output[i][1]))
189 {
190 indent = !indent;
191 out += sprintf("%3d. %s", i+1, output[i][0]);
192 if (!indent)
193 out += "\n";
194 else out += " | ";
195 }
196 }
197 if (indent)
198 out+="\n";
199 PL->More(out);
200 return 1;
201}
202
203// Liefert eine Liste der Objekte zurueck, die gerade im Storage liegen,
204// pro Blueprint jeweils eins.
205protected object* GetShopItems()
206{
207 object store = load_object(storage);
208 store->_register_shop(ME);
209 object* output = ({});
210 object* all_items = all_inventory(store);
211
212 // Wir brauchen eine Liste, die von jeder Blueprint nur einen Clone
213 // enthaelt. Daher werden die Ladenamen der Objekte als Keys im Mapping
214 // <items> verwendet und jeweils der aktuelle Clone als Value zugeordnet.
215 mapping items = m_allocate(sizeof(all_items));
216 foreach(object ob: all_items)
217 {
218 items[load_name(ob)] = ob;
219 }
220 // Die Fixed Objects werden ans Ende angehaengt, damit in dem Fall, dass
221 // ein Clone eines solchen Objektes im Lager liegt, dieser zuerst verkauft
222 // wird und nicht immer wieder ein neuer erstellt wird.
223 return m_values(items) + map(fixed_obj, #'load_object);
224}
225
MG Mud User88f12472016-06-24 23:31:02 +0200226// Kuemmert sich um die Listenausgabe fuer den Befehl "zeige"
227varargs protected int PrintList(string filter_fun, int liststyle)
228{
229 // Alle Items im Lager holen. Wenn keine vorhanden, tut uns das leid.
230 object *items_in_store = GetShopItems();
231 if ( !sizeof(items_in_store) ) {
232 write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
233 return 1;
234 }
235
236 // Das Listenformat ist von der Spielereingabe abhaengig. Wurde "lang"
237 // angefordert, geben wir einspaltig aus mit groesserer Spaltenbreite.
238 // Die Spaltenbreite wird dabei von dem Item mit dem laengsten Namen im
239 // gesamten Shop-Inventar bestimmt, damit nicht bei jeder Teilliste
240 // (Waffen, Ruestungen, Verschiedenes) unterschiedliche Breiten rauskommen.
241 //
242 // Der erste Parameter enthaelt die Katalognummer des Items, der zweite
243 // die Kurzbeschreibung, der dritte den Preis.
244 string listformat = "%3d. %-25.25s %6.6d";
245 if ( liststyle == LIST_LONG )
246 {
247 string* names = sort_array(
248 items_in_store->short(),
249 function int (string s1, string s2) {
250 return (sizeof(s1) < sizeof(s2));
251 });
252 // Wenn einspaltig ausgegeben wird, soll die Liste nicht beliebig breit
253 // werden. Daher wird die Short auf 65 Zeichen limitiert.
254 int len = 65;
255 if ( sizeof(names) )
256 len = min(len, sizeof(names[0]));
257 listformat = "%3d. %-"+len+"."+len+"s %6.6d";
258 }
259
260 string out="";
261 // Variablen, die innerhalb der Schleife benoetigt werden.
262 string kurz;
263 int indent, preis;
264 object item;
265 // Ueber die Liste laufen. <i> wird benoetigt, um die ausgegebene Liste
266 // konsistent numerieren zu koennen, damit kaufe <nr> funktioniert.
267 foreach(int i : sizeof(items_in_store))
268 {
269 item = items_in_store[i];
270 if ( call_other(ME, filter_fun, item) )
271 {
272 // Kurzbeschreibung und Preis ermitteln. Items ohne Short werden
273 // als "?" angezeigt.
274 kurz = (item->short() || "???")[0..<3];
275 preis = QueryBuyValue(item, PL);
276 // Beschreibung des Items anfuegen.
277 out += sprintf(listformat, i+1, kurz, preis);
278 indent = !indent;
279 // Wenn indent gesetzt ist, handelt es sich um das linke Item in der
280 // zweispaltigen Liste, dann fuegen wir einen Spaltentrenner an,
281 // ansonsten ist es das rechte, dann brechen wir um.
282 // Gilt natuerlich nur fuer kurze Listen.
283 out += ((indent && liststyle==LIST_SHORT)? " | " : "\n");
284 }
285 }
286 // Wenn die Liste eine ungerade Zahl Items enthaelt, ist in der letzten
287 // Zeile links ein Item aufgefuehrt, daher muss danach umbrochen werden.
288 // Gilt natuerlich nur fuer kurze Listen
289 if (indent && liststyle==LIST_SHORT)
290 out+="\n";
291
292 // Vor den Listen wird eine Info-Zeile ausgegeben, um gefilterte Listen
293 // kenntlich zu machen. Wird nach der Filterung des Inventars erzeugt,
294 // um eine leere Meldung ausgeben zu koennen, wenn nach Filterung nichts
295 // mehr uebrigbleibt.
296 string was;
297 switch(filter_fun)
298 {
299 case "IsArmour": was = "Ruestungen"; break;
300 case "IsWeapon": was = "Waffen"; break;
301 case "NoWeaponNoArmour":
302 was = (out==""?"sonstigen Waren":"sonstige Waren"); break;
303 default: was = "Waren"; break;
304 }
305 // <out> ist ein Leerstring, wenn keine Waren da sind, die dem Filterkri-
306 // terium entsprechen. Dann gibt's eine entsprechende Meldung.
307 if ( out == "" )
308 out = sprintf("Leider sind momentan keine %s im Angebot.\n", was);
309 else
310 out = sprintf("Folgende %s kannst Du hier kaufen:\n",was) + out;
311
312 PL->More(out);
313 return 1;
314}
315
316// Hilfsfunktionen zum Filtern des Ladeninventars fuer den "zeige"-Befehl
317static int AlwaysTrue(object ob)
318{ return 1; }
319
320static string IsWeapon(object ob)
321{ return ob->QueryProp(P_WEAPON_TYPE); }
322
323static string IsArmour(object ob)
324{ return ob->QueryProp(P_ARMOUR_TYPE); }
325
326static int NoWeaponNoArmour(object ob)
327{ return (!ob->QueryProp(P_WEAPON_TYPE) && !ob->QueryProp(P_ARMOUR_TYPE)); }
328
329
330// Diese Funktion ist oeffentlich, falls Magier abfragen wollen, ob ein laden
331// ein Objekt zerstoeren wuerde. Aber: Benutzung auf eigenes Risiko! Es wird
332// nicht garantiert, dass diese Funktion bzw. ihr Interface sich nicht
333// aendert.
334public int CheckForDestruct(object ob) // Pruefen, ob zerstoert werden soll
335{
336 string str;
337 /*
338 * P_NOBUY - Objekte auf jeden Fall zerstoeren
339 */
340 if(ob->QueryProp(P_NOBUY)) return 1;
341 /*
342 * Beschaedigte Objekte werden ebenfalls zerstoert
343 */
344 if(ob->QueryProp(P_DAMAGED)) return 1;
345 /*
346 * Ruestungen wenn sie a) eine DefendFunc definiert haben oder
347 * b) ueber der in KEEP_ARMOUR_CLASS definierten AC
348 * liegen (siehe /sys/combat.h)
349 */
350 if(str = IsArmour(ob))
351 {
352 if(objectp(ob->QueryProp(P_DEFEND_FUNC))) return 1;
353 if(ob->QueryProp(P_AC) >= KEEP_ARMOUR_CLASS[str]) return 1;
354 return 0;
355 }
356 /*
357 * Waffen wenn sie a) 1-haendig sind und eine WC > 120 besitzen oder
358 * b) 2-haendig sind und eine WC > 150 besitzen oder aber
359 * c) eine HitFunc definiert haben
360 */
361 if(str = IsWeapon(ob))
362 {
363 if(ob->QueryProp(P_NR_HANDS) > 1 && ob->QueryProp(P_WC) > 150) return 1;
364 if(ob->QueryProp(P_NR_HANDS) < 2 && ob->QueryProp(P_WC) > 120) return 1;
365 if(objectp(ob->QueryProp(P_HIT_FUNC))) return 1;
366 return 0;
367 }
368 return 0;
369}
370
371static int list(string str)
372{
373 _notify_fail(
374 "Bitte 'zeige', 'zeige waffen', 'zeige ruestungen' oder\n"
375 "'zeige verschiedenes' eingeben. Wenn Du das Schluesselwort 'lang'\n"
376 "oder '-1' anhaengst, wird die Liste einspaltig ausgegeben.\n");
377
378 if (!stringp(str) || !sizeof(str) )
379 return PrintList("AlwaysTrue");
380 if ( str == "lang" || str == "-1" )
381 return PrintList("AlwaysTrue", LIST_LONG);
382
383 string *params = explode(str," ");
384 if (sizeof(params[0])<3)
385 return 0;
386
387 int liststyle = LIST_SHORT;
388 if ( sizeof(params)>1 && params[1] == "lang" )
389 liststyle = LIST_LONG;
390
391 str=params[0][0..2];
392 if (str=="waf")
393 return PrintList("IsWeapon", liststyle);
394 if (str=="ver")
395 return PrintList("NoWeaponNoArmour", liststyle);
396 if (str=="rue")
397 return PrintList("IsArmour", liststyle);
398 return 0;
399}
400/*
401static varargs int QueryBuyValue(mixed ob, object client)
402{
403 if (objectp(ob))
404 return trading_price::QueryBuyValue(ob, client);
405 return (fixed_value[ob]*QueryBuyFact(client))/100;
406}
407*/
408
409static varargs int QueryBuyValue(object ob, object client)
410{
411 int fprice = fixed_value[load_name(ob)];
412
413 return (fprice>0) ?
414 fprice*QueryBuyFact(client)/100 :
415 trading_price::QueryBuyValue(ob, client);
416}
417
418static void UpdateCounter(object ob, int num)
419{
420 string tmp;
421
422 if (!num || !objectp(ob)) return;
423 tmp=BLUE_NAME(ob);
424 if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/")
425 tmp=ob->short()+tmp;
426 ob_anz[tmp] += num;
427 if (ob_anz[tmp] <= 0)
428 m_delete(ob_anz,tmp);
429}
430
431protected object FindInStore(string|int x)
432{
433 object* list = GetShopItems();
434 if ( intp(x) && x>0 && x<=sizeof(list) ) {
435 return list[x-1];
436 }
437 if (stringp(x))
438 {
439 if ( fixed_ids[x] )
440 return load_object(fixed_ids[x]);
441 list = filter_objects(list, "id", x);
442 if ( sizeof(list) )
443 return list[0];
444 // Wenn nix im Store gefunden (das schliesst eigentlicht schon die BPs der
445 // fixen Objekte ein, aber nicht, wenn diese nicht konfiguriert sind. D.h.
446 // diese Pruefung ist fuer nicht-konfigurierte BPs), Liste der
447 // FixedObjects pruefen unde so die eventuell manuell in
448 // AddFixedObject() angegebene Liste von IDs beruecksichtigen.
449 else if ( fixed_ids[x] )
450 return load_object(fixed_ids[x]);
451 }
452 return 0;
453}
454
455static string buy_obj(mixed ob, int short)
456{ return 0; }
457
458private void really_buy(int val, object pl, object ob, int u_req)
459{
460 // Staendig verfuegbare Objekte (fixed_obj) sind daran erkennbar, dass sie
461 // nicht im Lager liegen. Daher hier einen Clone erstellen, der dann
462 // stattdessen rausgegeben wird.
463 if ( !present(ob, find_object(storage)) )
464 ob = clone_object(ob);
465
466 // In Unitobjekten U_REQ (wieder) setzen (wegen input_to (bei dem sich das
467 // Kommandoverb aendert und deswegen U_REQ geloescht wird), und wegen
468 // Kaufens von Fixed-Objekt-Unitobjekten (bei diesen muss U_REQ _nach_ dem
469 // Clonen im Clone gesetzt werden, nicht automagisch in der BP durch den
470 // Aufruf von id() weiter vorher).
471 if (u_req>0)
472 {
473 // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
474 // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
475 // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
476 // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
477 // werden...
478 ob->QueryProp(U_REQ);
479 ob->SetProp(U_REQ, u_req);
480 }
481
482 pl->AddMoney(-val);
483 _add_money(val);
484
485 if (ob->move(pl,M_GET) != MOVE_OK) // Kann der Spieler das Objekt tragen?
486 {
487 write(break_string("Du kannst "+ob->name(WEN,1)+" nicht mehr tragen. "
488 "Ich lege "+ob->QueryPronoun(WEN)+" hier auf den Boden.",78,
489 Name(WER)+" sagt: "));
490 ob->move(ME,M_NOCHECK); // Nein :-)
491 }
492 else
493 {
494 // Falls das Objekt sich vereinigt hat, muss jetzt wieder U_REQ
495 // restauriert werden.
496 ob->SetProp(U_REQ, u_req);
497 write(break_string("Du kaufst "+ob->name(WEN,1)+".", 78));
498 }
499
500 say(break_string(PL->Name(WER)+" kauft "+ob->name(WEN)+".",78), ({PL}));
501 UpdateCounter(ob,-1);
502}
503
504static void ask_buy(string str, int val, object pl, object ob, int u_req)
505{
506 _notify_fail(break_string("Gut, Du kannst es Dir ja jederzeit "
507 "noch anders ueberlegen.",78,Name(WER)+" sagt: "));
508
509 if(!str || !stringp(str) || str == "nein" || str == "n")
510 {
511 return;
512 }
513 if(str != "ja" && str != "j")
514 {
515 return;
516 }
517 really_buy(val, pl, ob, u_req);
518}
519
520static int buy(string str)
521{
522 int i, val, par, dex;
523 mixed ob, res;
524 string dummy;
525
526 if (!str) {
527 write("Was willst Du kaufen?\n");
528 return 1;
529 }
530
531 if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
532 write("Es ist niemand da, der Dich bedienen koennte.\n");
533 return 1;
534 }
535
536 _notify_fail(break_string("Das kann ich in meinem Lager nicht finden.",78,
537 Name(WER)+" sagt: "));
538
539 // Um auch Teile von Unit-Stacks kaufen zu koennen, z.B. "kaufe 5 phiolen",
540 // darf hier zusaetzlich <dummy> nur ein Leerstring sein, sonst verzweigt
541 // die Syntaxpruefung hierhin und es wird das 5. Item der Liste gekauft.
542 if (sscanf(str,"%d%s",i,dummy)>0 && i>0 && !sizeof(dummy)) {
543 ob=FindInStore(i);
544 }
545 else ob=FindInStore(str);
546
547 if (!ob) return 0;
548
549 if (str = buy_obj(ob,0))
550 {
551 write(break_string(str,78,Name(WER)+" sagt: "));
552 return 1;
553 }
554
555 val = QueryBuyValue(ob,PL);
556
557 if (PL->QueryMoney() < val)
558 {
559 write(break_string(capitalize(ob->QueryPronoun(WER))+" wuerde "+val+
560 " Muenzen kosten, und Du hast nur "+PL->QueryMoney()+".",78,
561 Name(WER)+" bedauert: "));
562 return 1;
563 }
564
565 // Anzahl der angeforderten Einheiten vor dem Bewegen zwischenspeichern.
566 // Weil dabei im Fall von Units eine Vereinigung mit bereits im Inventar
567 // befindlichen Einheiten stattfindet, muss das ggf. nach Bewegen
568 // zurueckgesetzt werden.
569 int u_req = ob->QueryProp(U_REQ);
570
571 if ((res = ob->QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
572 (res = call_other("/std/restriction_checker",
573 "check_restrictions",PL,res)) &&
574 stringp(res))
575 {
576 _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
577 "verwenden. Grund: "+res+"Moechtest Du "+ob->QueryPronoun(WEN)+
578 " dennoch kaufen?",78,Name(WER)+" sagt: "));
579
580 input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
581 return 0;
582 }
583
Arathorn26e02d32019-12-02 23:55:09 +0100584 par = ob->QueryProp(P_PARRY);
585 dex = PL->QueryAttribute(A_DEX);
MG Mud User88f12472016-06-24 23:31:02 +0200586
587 if ((((par < PARRY_ONLY) && ((dex + 8) * 10) < ob->QueryProp(P_WC)) ||
588 ((par > PARRY_NOT) && ((dex + 5) * 2) < ob->QueryProp(P_AC))) &&
589 VALID_WEAPON_TYPE(ob))
590 {
591 _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
592 "zuecken, da Deine Geschicklichkeit dafuer nicht ausreicht. Moechtest "
593 "Du "+ob->QueryPronoun(WEN)+" dennoch kaufen?",78,
594 Name(WER)+" sagt: "));
595
596 input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
597 return 0;
598 }
599
600 really_buy(val, PL, ob, u_req);
601
602 return 1;
603}
604
605private void give_money(int value)
606// Geld gutschreiben...
607{
608 if (!value) return;
609 write(break_string(Name(WER, 1)+" zahlt Dir "+value+" Goldstueck"
610 +(value==1?".":"e."), 78));
611 if ((PL->AddMoney(value))<=0) {
612 object mon;
613
614 write("Du kannst das Geld nicht mehr tragen!\n");
615 mon=clone_object(GELD);
616 mon->SetProp(P_AMOUNT,value);
617 mon->move(ME,M_MOVE_ALL|M_NOCHECK);
618 }
619}
620
621static int make_to_money(object ob, int value)
622// Interne Funktion, die ob versucht in das Lager zu ueberfuehren und das
623// Geld das dabei fuer den Spieler abfaellt zurueckliefert.
624{
MG Mud User88f12472016-06-24 23:31:02 +0200625 int ret;
626
627 if (!objectp(ob) || environment(ob)==find_object(storage)) {
628 write(break_string(Name(WER, 1)+" wundert sich ueber Dich.", 78));
629 return 0;
630 }
631 if (value>QueryProp(P_CURRENT_MONEY)) {
632 write(break_string("Ich hab das Geld leider nicht mehr.", 78,
633 Name(WER, 1)+" sagt: "));
634 return 0;
635 }
636 // U_REQ merken, falls sich Objekte vereinigen. Sonst stimmt nicht nur der
637 // Name, sondern es werden ggf. auch die falsche Anzahl zerstoert.
638 //TOOO: Oder Units entsorgen und als Feature deklarieren?
639 int req = ob->QueryProp(U_REQ);
640 if (CheckForDestruct(ob) > 0) // soll ob zerstoert werden?
641 {
642 ret = ob->move(storage,M_PUT|M_GET);
643 // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
644 // Einheiten restauriert werden.
645 // Problem: falls das verkaufte Objekt Units hat, beschaedigt ist und sich
646 // vereinigt hat, sind jetzt leider alle Einheiten im Lager beschaedigt.
647 // Das ist unschoen - aber mir jetzt zuviel AUfwand, das korrekt zu bauen,
648 // weil es nur sehr selten vorkommt. (Hint: separater Muellraum)
649 ob->SetProp(U_REQ, req);
650 if (ret > 0) // Sonst werden auch Sachen zerstoert, die man nicht
651 { // weglegen kann
652 say(break_string(PL->Name()+" verkauft "+ob->name(WEN)+".", 78));
653 if(ob->QueryProp(P_DAMAGED)) // Andere Meldung bei beschaedigten
654 { // Objekten ...
655 write(break_string(Name(WER,1)+" findet zwar keinen Gefallen an "
656 +ob->name(WEM,1)+", nimmt "+ob->QueryPronoun(WEN)+" Dir zuliebe "
657 "aber trotzdem.",78));
658 }
659 else
660 {
661 write(break_string(Name(WER, 1)+" findet Gefallen an "
662 +ob->name(WEM, 1) + " und legt "+ob->QueryPronoun(WEN)
663 +" zu "+(QueryProp(P_GENDER)==FEMALE?"ihren":"seinen")
664 +" Privatsachen.", 78));
665 }
666 /* Er zahlt Dir "+value+" Muenze"+(value==1?"":"n")+" dafuer.", 78)); */
667 _add_money(-value);
668 _add_money(value*QueryProp(P_SHOP_PERCENT_LEFT)/100); // Wegen Zerstoerung des Objektes
669 UpdateCounter(ob,1);
670 ob->remove(1);
671 return value;
672 }
Arathorna1fc9352019-12-03 00:06:39 +0100673 else if (ret == ME_CANT_BE_DROPPED)
674 {
675 string|int nodrop = ob->QueryProp(P_NODROP);
676 if (stringp(nodrop) && sizeof(nodrop))
677 write(nodrop);
678 else
679 write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!",
680 78));
MG Mud User88f12472016-06-24 23:31:02 +0200681 return 0;
682 }
683 else
684 write(break_string(ob->Name(WER)+" interessiert mich nicht.", 78,
685 Name(WER, 1)+" sagt: "));
686 }
687 else if ((ret=(ob->move(storage,M_PUT|M_GET)))>0)
688 {
689 // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
690 // Einheiten restauriert werden.
691 ob->SetProp(U_REQ, req);
692 say(break_string(PL->Name(WER)+" verkauft "+ob->name(WEN)+".", 78));
693 _add_money(-value);
694 UpdateCounter(ob,1);
695 return value;
696 }
Arathorna1fc9352019-12-03 00:06:39 +0100697 else if (ret == ME_CANT_BE_DROPPED)
698 {
699 string|int nodrop = ob->QueryProp(P_NODROP);
700 if (stringp(nodrop) && sizeof(nodrop))
701 write(nodrop);
702 else
703 write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!",
704 78));
705 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200706 }
Arathorna1fc9352019-12-03 00:06:39 +0100707 else
708 write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!",
709 78));
MG Mud User88f12472016-06-24 23:31:02 +0200710 return 0;
711}
712
713static void ask_sell(string str, object ob, int val, int u_req)
714// Wenn ein einzelnen Stueck unter Wert verkauft werden soll, wird nachgefragt
715// u_req ist bei Unitobjekten die Anzahl an zu verkaufenden Einheiten. Bei
716// normalen Objekten ist u_req 0.
717{
718 str=lower_case(str||"");
719 if (str=="ja"||str=="j")
720 {
721 // In Unitobjekten U_REQ (wieder) setzen.
722 if (u_req>0)
723 {
724 // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
725 // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
726 // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
727 // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
728 // werden...
729 ob->QueryProp(U_REQ);
730 ob->SetProp(U_REQ, u_req);
731 }
732 give_money(make_to_money(ob,val));
733 }
734 else
735 write(break_string("Ok, dann behalts!", 78,
736 Name(WER, 1)+" sagt: "));
737}
738
739static string sell_obj(object ob, int short)
740// Ist der Haendler bereit ob zu kaufen? wenn nein, Rueckgabe einer Meldung,
741// die der Haendler sagen soll.
742{ mixed nosell;
743
744 if (BLUE_NAME(ob)==GELD)
745 return "Das waere ja wohl Unsinn, oder ...?";
746 if (nosell=ob->QueryProp(P_NOSELL))
747 {
748 if (stringp(nosell))
749 return nosell;
750 return ("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!");
751 }
752 if (ob->QueryProp(P_CURSED))
753 return ob->Name(WER,1)
754 +" ist mir irgendwie ungeheuer! Das kannst Du nicht verkaufen!";
755 // man sollte keine COntainer mit Inhalt verkaufen koennen, ggf. kauft sie
756 // dann ein anderer Spieler.
757 if (first_inventory(ob))
758 {
759 return ob->Name(WER, 1) + " ist nicht leer!";
760 }
761 return 0;
762}
763
764static varargs int sell(string str, int f)
765{
766 int i, val, oval, tmp;
767 object *obs;
768
769 if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER),ME)) {
770 write("Es ist niemand da, der Dich bedienen koennte.\n");
771 return 1;
772 }
773
774 if (!str) {
775 notify_fail("Was moechtest Du denn verkaufen?\n");
776 return 0;
777 }
778
779 /* Ergebnis von find_obs() sollte unifiziert werden, damit ein mehrfach
780 gefundenes Objekt nicht mehrfach versucht wird zu verkaufen.
781 Beispiel: Objekt hat P_NOBUY gesetzt und mehrere IDs gesetzt. Wenn
782 ein Spieler es mit "verkaufe ID1 und ID2" versucht zu verkaufen,
783 wuerde das einen Bug ausloesen. Derselbe Bug entsteht, wenn man mit
784 "verkaufe ID1 und ID1" verkauft. */
785 obs = PL->find_obs(str, PUT_GET_DROP);
786 /* Erst im Inventar schauen, dann im Environment. find_obs() ohne 2.
787 Parameter macht das standardmaessig andersherum.
788 TODO: Aenderung ueberpruefen, sobald das neue put_and_get.c
789 eingebaut wurde. */
790 if ( !sizeof(obs) )
791 obs = PL->find_obs(str, PUT_GET_TAKE) || ({});
792 obs = m_indices(mkmapping(obs));
793 if (!i=sizeof(obs)) {
794 notify_fail("Was moechtest Du denn verkaufen?\n");
795 return 0;
796 }
797 call_other(storage, "_register_shop", ME);
798 if (i==1) {
799 if (str=sell_obj(obs[0], 0)) {
800 write(break_string(str, 78, Name(WER, 1)+" sagt: "));
801 return 1;
802 }
803 if ((oval=obs[0]->QueryProp(P_VALUE))<=0) {
804 write(break_string(obs[0]->Name(WER)
805 +(obs[0]->QueryProp(P_PLURAL) ? " haben" : " hat")
806 +" keinen materiellen Wert, tut mir leid.", 78,
807 Name(WER, 1)+" sagt: "));
808 return 1;
809 }
810 val=QuerySellValue(obs[0], PL);
811 if (!val) {
812 write(break_string("Ich bin absolut pleite. Tut mir aufrichtig leid.",
813 78, Name(WER, 1)+" sagt: "));
814 return 1;
815 }
816 if (val==oval || f) {
817 give_money(make_to_money(obs[0], val));
818 return 1;
819 }
820 if (str=obs[0]->QueryProp(P_NODROP)) {
821 if (stringp(str))
822 write(str);
823 else write(break_string("Du kannst "+obs[0]->name(WEN,1)
824 +" nicht verkaufen!", 78));
825 return 1;
826 }
827
828 if (obs[0]->QueryProp(P_DAMAGED)) // Bei beschaedigten Objekten gibt
829 { // es auch hier eine andere Meldung
830 write(break_string("Da "+obs[0]->name(WER)+" beschaedigt "
831 +(obs[0]->QueryProp(P_PLURAL)?"sind":"ist")+", kann ich Dir "
832 "nur "+val+" Muenze"+(val == 1?"":"n")+" dafuer bieten. Und "
833 "damit mache ich noch Verlust! Nimmst Du mein Angebot an? "
834 "(ja/nein)",78,Name(WER,1)+" sagt: "));
835 }
836 else // Default
837 {
838 write(break_string(Name(WER, 1)+" sagt: "
839 +"Nach der aktuellen Marktlage kann ich Dir dafuer nur "
840 +val+" Muenze"+(val==1?"":"n")+" bieten, obwohl "
841 +obs[0]->name(WER)+" eigentlich "+oval+" Muenze"
842 +(oval==1?"":"n")+" wert waere. Willst Du "
843 +(QueryProp(P_PLURAL) ? "sie" : "es")
844 +" mir dafuer ueberlassen?", 78));
845 }
846 // in ask_sell() gibt es das query_verb() nicht mehr, weswegen U_REQ in
847 // Unitobjekten zurueckgesetzt wird. Damit geht die info verloren,
848 // wieviele Objekte der Spieler angegeben hat. Diese muss gerettet und
849 // via ask_sell() in make_to_money() ankommen. In normalen Objekten ist
850 // U_REQ 0.
851 input_to("ask_sell",INPUT_PROMPT, "(ja/nein) ",obs[0], val,
852 (obs[0])->QueryProp(U_REQ) );
853 return 1;
854 }
855 for (--i; i>=0 && get_eval_cost()>50000; i--) {
856 if (oval=obs[i]->QueryProp(P_VALUE)) {
857 if (obs[i]->QueryProp(P_KEEP_ON_SELL)==getuid(PL)
858 || obs[i]->QueryProp(P_WORN) || obs[i]->QueryProp(P_WIELDED))
859 write(break_string(obs[i]->Name(WER)+": Du behaeltst "
860 +obs[i]->name(WEN)+".", 78));
861 else if (str=sell_obj(obs[i], 1))
862 write(break_string(obs[i]->Name(WER)+": "+str, 78));
863 else {
864 tmp=QuerySellValue(obs[i], PL);
865 if (!tmp) {
866 write(break_string(
867 "Ich bin absolut pleite. Tut mir aufrichtig leid.", 78,
868 Name(WER, 1)+" sagt: "));
869 break;
870 }
871 else if (!f && tmp*10<oval)
872 write(break_string(obs[i]->Name(WER)+": "+Name(WER, 1)
873 +" bietet Dir aber nur "+tmp+" Goldstueck"
874 +(tmp>1 ? "e" : "")+" dafuer.", 78));
875 else {
876 str=(obs[i]->Name(WER));
877 if (tmp=make_to_money(obs[i], tmp)) {
878 write(break_string(str+": "+Name(WER, 1)
879 +" gibt Dir dafuer "+tmp+" Goldstueck"
880 +(tmp==1?".":"e."), 78));
881 val+=tmp;
882 }
883 }
884 }
885 }
886 }
887 if (!val)
888 write(break_string("Hmmm, Du hast aber nicht besonders viel zu bieten...",
889 78, Name(WER)+" sagt: "));
890 else give_money(val);
891 return 1;
892}
893
894static int force_sell(string str)
895{ return sell(str, 1); }
896
897static int evaluate(string str)
898{
899 object ob;
900 int val,rval;
901
902 if (!str) return 0;
903 if(stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
904 write("Es ist niemand da, der Dich bedienen koennte.\n");
905 return 1;
906 }
907
908 ob=present(str, ME);
909 if (!ob) ob=deep_present(str,PL);
910 if (!ob) {
911 write("Hm? "+capitalize(str)+"? Wovon redest Du?\n");
912 return 1;
913 }
914 if (living(ob)) {
915 _notify_fail("Nanu, seit wann werden hier Lebewesen verkauft?\n");
916 return 0;
917 }
918 if (str=sell_obj(ob, 0)) {
919 write(break_string(str, 78, Name(WER)+" sagt: "));
920 return 1;
921 }
922 rval=ob->QueryProp(P_VALUE);
923 if (rval) {
924 val=QuerySellValue(ob, PL);
925 if (rval==val) {
926 tell_object(this_player(),break_string(
927 "Naja, ich denke, " +val+ " Muenze"
928 + (val==1 ? "" : "n")
929 + " waere"+(ob->QueryProp(P_AMOUNT)>1?"n ":" ")
930 + (ob->QueryPronoun(WER))+" schon wert.\n",78));
931 }
932 else if (val) {
933 tell_object(this_player(),break_string(
934 "Oh, nach der aktuellen Marktlage kann ich nur "+val+" Muenze"+
935 (val==1?"":"n")+" bezahlen, obwohl "
936 + (QueryProp(P_PLURAL) ? "sie" : "es")
937 + " eigentlich "+rval
938 + " Muenze"+(rval==1?"":"n")+" wert ist.\n",78));
939 }
940 else write("Ich bin vollkommen pleite. Tut mir leid.\n");
941 }
942 else write("Das ist voellig wertlos.\n");
943 return 1;
944}
945
946static int show_obj(string str)
947{
948 int i;
949 string was;
950 mixed ob;
951
952 if (!str) return 0;
953 if (sscanf(str,"%s im laden",was)>0 || sscanf(str,"%s in laden",was)>0) {
954 _notify_fail("Das kann ich im Lager nicht finden.\n");
955 ob=FindInStore(was);
956 } else if (sscanf(str,"%d",i)>0 && i>0) {
957 _notify_fail("Das kann ich im Lager nicht finden.\n");
958 ob=FindInStore(i);
959 }
960 if (!ob) return 0;
961 write(ob->Name(WER)+":\n"+ob->long()+capitalize(ob->QueryPronoun())
962 +" kostet "+QueryBuyValue(ob,PL)+" Muenzen.\n");
963 return 1;
964}
965
966// benutzt von trading_price::QueryValue(object, int, object)
967static int ObjectCount(object ob)
968{
969 string tmp;
970
971 if (!objectp(ob)) return 0;
972 tmp = BLUE_NAME(ob);
973 if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/") tmp=ob->short()+tmp;
974 return ob_anz[tmp];
975}
976
977// benutzt von trading_price::QuerySellValue(object, object)
978static varargs int QueryValue(object ob, int value, object client)
979{
980 int new_value, mymoney;
981
982 if (!objectp(ob)) return 0;
983 if (Query("laden::compat")) {
984 new_value=(value>1000?1000:value);
985 mymoney = QueryProp(P_CURRENT_MONEY);
986 if (new_value>mymoney)
987 return (mymoney>0?mymoney:0);
988 else return new_value;
989 }
990 return ::QueryValue(ob, value, client);
991}
992
993void reset()
994{
995 mixed *keys;
996 int i;
997
998 trading_price::reset();
999
1000 if (!mappingp(ob_anz))
1001 return;
1002 keys=m_indices(ob_anz);
1003 for (i=sizeof(keys)-1;i>=0;i--) {
1004 ob_anz[keys[i]]=ob_anz[keys[i]]*7/8;
1005 if (!ob_anz[keys[i]])
1006 m_delete(ob_anz,keys[i]);
1007 }
1008}
1009
1010varargs int CheckFindRestrictions(object ob, mixed restr, closure qp) {
1011 return 0;
1012}
1013
1014int EvalWeapon(object ob, closure qp) {
1015 int wc,val;
1016
1017 wc=funcall(qp,P_WC);
1018 val=funcall(qp,P_EFFECTIVE_WC);
1019 if (val>wc) wc=val;
1020 return wc;
1021}
1022
1023varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
1024 int bestwc, mixed restr) {
1025 object bestob,ob;
1026 string otype;
1027 int wc,bestval,val,w,bestw;
1028 closure qp;
1029
1030 if (!stringp(storage) || !objectp(ob=find_object(storage))) return 0;
1031 if (!maxmon) maxmon=100000;
1032 if (!maxw) maxw=75000;
1033 if (!hands) hands=2;
1034 if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
1035 if (type && !pointerp(type) && !mappingp(type)) type=({type});
1036
1037 for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
1038 qp=symbol_function("QueryProp",ob);
1039 if (!otype=funcall(qp,P_WEAPON_TYPE)) continue;
1040 if (type && member(type,otype)<0) continue;
1041 wc=EvalWeapon(ob,qp);
1042 if (wc<bestwc) continue;
1043 if (funcall(qp,P_NR_HANDS)>hands) continue;
1044 w=funcall(qp,P_WEIGHT);
1045 if (w>maxw) continue;
1046 val=funcall(qp,P_VALUE);
1047 if (val>maxmon) continue;
1048 if (bestob && wc<=bestwc) {
1049 if (val>bestval) continue;
1050 else if (val==bestval && w>bestw) continue;
1051 }
1052 if (val>bestval && bestob && wc<=bestwc) continue;
1053 if (CheckFindRestrictions(ob,restr,qp)) continue;
1054 bestob=ob;
1055 bestwc=wc;
1056 bestval=val;
1057 bestw=w;
1058 }
1059 return bestob;
1060}
1061
1062int EvalArmour(object ob,closure qp) {
1063 int ac,val;
1064
1065 ac=funcall(qp,P_AC);
1066 val=funcall(qp,P_EFFECTIVE_AC);
1067 if (val>ac) ac=val;
1068 return ac;
1069}
1070
1071varargs mapping FindBestArmoursT(mixed type, int maxmon, int maxw,
1072 mapping bestac, mixed restr) {
1073 object ob;
1074 string otype;
1075 int ac,val,sum,w,wsum;
1076 mapping bestob,bestval,bestw;
1077 closure qp;
1078
1079 if (!stringp(storage) || !objectp(ob=find_object(storage))) return ([]);
1080 if (!maxmon) maxmon=100000;
1081 if (!maxw) maxw=75000;
1082 if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
1083 if (type && !pointerp(type) && !mappingp(type)) type=({type});
1084 if (!mappingp(bestac)) bestac=([]);
1085 bestob=([]);bestval=([]);bestw=([]);
1086
1087 for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
1088 qp=symbol_function("QueryProp",ob);
1089 if (!otype=funcall(qp,P_ARMOUR_TYPE)) continue;
1090 if (type && member(type,otype)<0) continue;
1091 ac=EvalArmour(ob,qp);
1092 if (ac<bestac[otype]) continue;
1093 w=funcall(qp,P_WEIGHT);
1094 if (wsum-bestw[otype]+w>maxw) continue;
1095 val=funcall(qp,P_VALUE);
1096 if (sum-bestval[otype]+val>maxmon) continue;
1097 if (bestob[otype] && ac<=bestac[otype]) {
1098 if (val>bestval[otype]) continue;
1099 else if (val==bestval[otype] && w>bestw[otype]) continue;
1100 }
1101 if (CheckFindRestrictions(ob,restr,qp)) continue;
1102 sum=sum-bestval[otype]+val;
1103 wsum=wsum-bestw[otype]+w;
1104 bestob[otype]=ob;
1105 bestac[otype]=ac;
1106 bestval[otype]=val;
1107 bestw[otype]=w;
1108 }
1109 return bestob;
1110}
1111
1112varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
1113 mapping bestac, mixed restr) {
1114 return m_values(FindBestArmoursT(type,maxmon,maxw,bestac,restr));
1115}