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