blob: 896b219ac4d51710585cbed84434355ce48e291d [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{
625 string str;
626 int ret;
627
628 if (!objectp(ob) || environment(ob)==find_object(storage)) {
629 write(break_string(Name(WER, 1)+" wundert sich ueber Dich.", 78));
630 return 0;
631 }
632 if (value>QueryProp(P_CURRENT_MONEY)) {
633 write(break_string("Ich hab das Geld leider nicht mehr.", 78,
634 Name(WER, 1)+" sagt: "));
635 return 0;
636 }
637 // U_REQ merken, falls sich Objekte vereinigen. Sonst stimmt nicht nur der
638 // Name, sondern es werden ggf. auch die falsche Anzahl zerstoert.
639 //TOOO: Oder Units entsorgen und als Feature deklarieren?
640 int req = ob->QueryProp(U_REQ);
641 if (CheckForDestruct(ob) > 0) // soll ob zerstoert werden?
642 {
643 ret = ob->move(storage,M_PUT|M_GET);
644 // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
645 // Einheiten restauriert werden.
646 // Problem: falls das verkaufte Objekt Units hat, beschaedigt ist und sich
647 // vereinigt hat, sind jetzt leider alle Einheiten im Lager beschaedigt.
648 // Das ist unschoen - aber mir jetzt zuviel AUfwand, das korrekt zu bauen,
649 // weil es nur sehr selten vorkommt. (Hint: separater Muellraum)
650 ob->SetProp(U_REQ, req);
651 if (ret > 0) // Sonst werden auch Sachen zerstoert, die man nicht
652 { // weglegen kann
653 say(break_string(PL->Name()+" verkauft "+ob->name(WEN)+".", 78));
654 if(ob->QueryProp(P_DAMAGED)) // Andere Meldung bei beschaedigten
655 { // Objekten ...
656 write(break_string(Name(WER,1)+" findet zwar keinen Gefallen an "
657 +ob->name(WEM,1)+", nimmt "+ob->QueryPronoun(WEN)+" Dir zuliebe "
658 "aber trotzdem.",78));
659 }
660 else
661 {
662 write(break_string(Name(WER, 1)+" findet Gefallen an "
663 +ob->name(WEM, 1) + " und legt "+ob->QueryPronoun(WEN)
664 +" zu "+(QueryProp(P_GENDER)==FEMALE?"ihren":"seinen")
665 +" Privatsachen.", 78));
666 }
667 /* Er zahlt Dir "+value+" Muenze"+(value==1?"":"n")+" dafuer.", 78)); */
668 _add_money(-value);
669 _add_money(value*QueryProp(P_SHOP_PERCENT_LEFT)/100); // Wegen Zerstoerung des Objektes
670 UpdateCounter(ob,1);
671 ob->remove(1);
672 return value;
673 }
674 else if (ret == ME_CANT_BE_DROPPED) {
675 if ((str=ob->QueryProp(P_NODROP)) && stringp(str)) {
676 write(str);
677 return 0;
678 }
679 write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
680 return 0;
681 }
682 else
683 write(break_string(ob->Name(WER)+" interessiert mich nicht.", 78,
684 Name(WER, 1)+" sagt: "));
685 }
686 else if ((ret=(ob->move(storage,M_PUT|M_GET)))>0)
687 {
688 // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
689 // Einheiten restauriert werden.
690 ob->SetProp(U_REQ, req);
691 say(break_string(PL->Name(WER)+" verkauft "+ob->name(WEN)+".", 78));
692 _add_money(-value);
693 UpdateCounter(ob,1);
694 return value;
695 }
696 else if (ret == ME_CANT_BE_DROPPED) {
697 if ((str=ob->QueryProp(P_NODROP)) && stringp(str))
698 write(str);
699 else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
700 }
701 else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
702 return 0;
703}
704
705static void ask_sell(string str, object ob, int val, int u_req)
706// Wenn ein einzelnen Stueck unter Wert verkauft werden soll, wird nachgefragt
707// u_req ist bei Unitobjekten die Anzahl an zu verkaufenden Einheiten. Bei
708// normalen Objekten ist u_req 0.
709{
710 str=lower_case(str||"");
711 if (str=="ja"||str=="j")
712 {
713 // In Unitobjekten U_REQ (wieder) setzen.
714 if (u_req>0)
715 {
716 // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
717 // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
718 // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
719 // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
720 // werden...
721 ob->QueryProp(U_REQ);
722 ob->SetProp(U_REQ, u_req);
723 }
724 give_money(make_to_money(ob,val));
725 }
726 else
727 write(break_string("Ok, dann behalts!", 78,
728 Name(WER, 1)+" sagt: "));
729}
730
731static string sell_obj(object ob, int short)
732// Ist der Haendler bereit ob zu kaufen? wenn nein, Rueckgabe einer Meldung,
733// die der Haendler sagen soll.
734{ mixed nosell;
735
736 if (BLUE_NAME(ob)==GELD)
737 return "Das waere ja wohl Unsinn, oder ...?";
738 if (nosell=ob->QueryProp(P_NOSELL))
739 {
740 if (stringp(nosell))
741 return nosell;
742 return ("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!");
743 }
744 if (ob->QueryProp(P_CURSED))
745 return ob->Name(WER,1)
746 +" ist mir irgendwie ungeheuer! Das kannst Du nicht verkaufen!";
747 // man sollte keine COntainer mit Inhalt verkaufen koennen, ggf. kauft sie
748 // dann ein anderer Spieler.
749 if (first_inventory(ob))
750 {
751 return ob->Name(WER, 1) + " ist nicht leer!";
752 }
753 return 0;
754}
755
756static varargs int sell(string str, int f)
757{
758 int i, val, oval, tmp;
759 object *obs;
760
761 if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER),ME)) {
762 write("Es ist niemand da, der Dich bedienen koennte.\n");
763 return 1;
764 }
765
766 if (!str) {
767 notify_fail("Was moechtest Du denn verkaufen?\n");
768 return 0;
769 }
770
771 /* Ergebnis von find_obs() sollte unifiziert werden, damit ein mehrfach
772 gefundenes Objekt nicht mehrfach versucht wird zu verkaufen.
773 Beispiel: Objekt hat P_NOBUY gesetzt und mehrere IDs gesetzt. Wenn
774 ein Spieler es mit "verkaufe ID1 und ID2" versucht zu verkaufen,
775 wuerde das einen Bug ausloesen. Derselbe Bug entsteht, wenn man mit
776 "verkaufe ID1 und ID1" verkauft. */
777 obs = PL->find_obs(str, PUT_GET_DROP);
778 /* Erst im Inventar schauen, dann im Environment. find_obs() ohne 2.
779 Parameter macht das standardmaessig andersherum.
780 TODO: Aenderung ueberpruefen, sobald das neue put_and_get.c
781 eingebaut wurde. */
782 if ( !sizeof(obs) )
783 obs = PL->find_obs(str, PUT_GET_TAKE) || ({});
784 obs = m_indices(mkmapping(obs));
785 if (!i=sizeof(obs)) {
786 notify_fail("Was moechtest Du denn verkaufen?\n");
787 return 0;
788 }
789 call_other(storage, "_register_shop", ME);
790 if (i==1) {
791 if (str=sell_obj(obs[0], 0)) {
792 write(break_string(str, 78, Name(WER, 1)+" sagt: "));
793 return 1;
794 }
795 if ((oval=obs[0]->QueryProp(P_VALUE))<=0) {
796 write(break_string(obs[0]->Name(WER)
797 +(obs[0]->QueryProp(P_PLURAL) ? " haben" : " hat")
798 +" keinen materiellen Wert, tut mir leid.", 78,
799 Name(WER, 1)+" sagt: "));
800 return 1;
801 }
802 val=QuerySellValue(obs[0], PL);
803 if (!val) {
804 write(break_string("Ich bin absolut pleite. Tut mir aufrichtig leid.",
805 78, Name(WER, 1)+" sagt: "));
806 return 1;
807 }
808 if (val==oval || f) {
809 give_money(make_to_money(obs[0], val));
810 return 1;
811 }
812 if (str=obs[0]->QueryProp(P_NODROP)) {
813 if (stringp(str))
814 write(str);
815 else write(break_string("Du kannst "+obs[0]->name(WEN,1)
816 +" nicht verkaufen!", 78));
817 return 1;
818 }
819
820 if (obs[0]->QueryProp(P_DAMAGED)) // Bei beschaedigten Objekten gibt
821 { // es auch hier eine andere Meldung
822 write(break_string("Da "+obs[0]->name(WER)+" beschaedigt "
823 +(obs[0]->QueryProp(P_PLURAL)?"sind":"ist")+", kann ich Dir "
824 "nur "+val+" Muenze"+(val == 1?"":"n")+" dafuer bieten. Und "
825 "damit mache ich noch Verlust! Nimmst Du mein Angebot an? "
826 "(ja/nein)",78,Name(WER,1)+" sagt: "));
827 }
828 else // Default
829 {
830 write(break_string(Name(WER, 1)+" sagt: "
831 +"Nach der aktuellen Marktlage kann ich Dir dafuer nur "
832 +val+" Muenze"+(val==1?"":"n")+" bieten, obwohl "
833 +obs[0]->name(WER)+" eigentlich "+oval+" Muenze"
834 +(oval==1?"":"n")+" wert waere. Willst Du "
835 +(QueryProp(P_PLURAL) ? "sie" : "es")
836 +" mir dafuer ueberlassen?", 78));
837 }
838 // in ask_sell() gibt es das query_verb() nicht mehr, weswegen U_REQ in
839 // Unitobjekten zurueckgesetzt wird. Damit geht die info verloren,
840 // wieviele Objekte der Spieler angegeben hat. Diese muss gerettet und
841 // via ask_sell() in make_to_money() ankommen. In normalen Objekten ist
842 // U_REQ 0.
843 input_to("ask_sell",INPUT_PROMPT, "(ja/nein) ",obs[0], val,
844 (obs[0])->QueryProp(U_REQ) );
845 return 1;
846 }
847 for (--i; i>=0 && get_eval_cost()>50000; i--) {
848 if (oval=obs[i]->QueryProp(P_VALUE)) {
849 if (obs[i]->QueryProp(P_KEEP_ON_SELL)==getuid(PL)
850 || obs[i]->QueryProp(P_WORN) || obs[i]->QueryProp(P_WIELDED))
851 write(break_string(obs[i]->Name(WER)+": Du behaeltst "
852 +obs[i]->name(WEN)+".", 78));
853 else if (str=sell_obj(obs[i], 1))
854 write(break_string(obs[i]->Name(WER)+": "+str, 78));
855 else {
856 tmp=QuerySellValue(obs[i], PL);
857 if (!tmp) {
858 write(break_string(
859 "Ich bin absolut pleite. Tut mir aufrichtig leid.", 78,
860 Name(WER, 1)+" sagt: "));
861 break;
862 }
863 else if (!f && tmp*10<oval)
864 write(break_string(obs[i]->Name(WER)+": "+Name(WER, 1)
865 +" bietet Dir aber nur "+tmp+" Goldstueck"
866 +(tmp>1 ? "e" : "")+" dafuer.", 78));
867 else {
868 str=(obs[i]->Name(WER));
869 if (tmp=make_to_money(obs[i], tmp)) {
870 write(break_string(str+": "+Name(WER, 1)
871 +" gibt Dir dafuer "+tmp+" Goldstueck"
872 +(tmp==1?".":"e."), 78));
873 val+=tmp;
874 }
875 }
876 }
877 }
878 }
879 if (!val)
880 write(break_string("Hmmm, Du hast aber nicht besonders viel zu bieten...",
881 78, Name(WER)+" sagt: "));
882 else give_money(val);
883 return 1;
884}
885
886static int force_sell(string str)
887{ return sell(str, 1); }
888
889static int evaluate(string str)
890{
891 object ob;
892 int val,rval;
893
894 if (!str) return 0;
895 if(stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
896 write("Es ist niemand da, der Dich bedienen koennte.\n");
897 return 1;
898 }
899
900 ob=present(str, ME);
901 if (!ob) ob=deep_present(str,PL);
902 if (!ob) {
903 write("Hm? "+capitalize(str)+"? Wovon redest Du?\n");
904 return 1;
905 }
906 if (living(ob)) {
907 _notify_fail("Nanu, seit wann werden hier Lebewesen verkauft?\n");
908 return 0;
909 }
910 if (str=sell_obj(ob, 0)) {
911 write(break_string(str, 78, Name(WER)+" sagt: "));
912 return 1;
913 }
914 rval=ob->QueryProp(P_VALUE);
915 if (rval) {
916 val=QuerySellValue(ob, PL);
917 if (rval==val) {
918 tell_object(this_player(),break_string(
919 "Naja, ich denke, " +val+ " Muenze"
920 + (val==1 ? "" : "n")
921 + " waere"+(ob->QueryProp(P_AMOUNT)>1?"n ":" ")
922 + (ob->QueryPronoun(WER))+" schon wert.\n",78));
923 }
924 else if (val) {
925 tell_object(this_player(),break_string(
926 "Oh, nach der aktuellen Marktlage kann ich nur "+val+" Muenze"+
927 (val==1?"":"n")+" bezahlen, obwohl "
928 + (QueryProp(P_PLURAL) ? "sie" : "es")
929 + " eigentlich "+rval
930 + " Muenze"+(rval==1?"":"n")+" wert ist.\n",78));
931 }
932 else write("Ich bin vollkommen pleite. Tut mir leid.\n");
933 }
934 else write("Das ist voellig wertlos.\n");
935 return 1;
936}
937
938static int show_obj(string str)
939{
940 int i;
941 string was;
942 mixed ob;
943
944 if (!str) return 0;
945 if (sscanf(str,"%s im laden",was)>0 || sscanf(str,"%s in laden",was)>0) {
946 _notify_fail("Das kann ich im Lager nicht finden.\n");
947 ob=FindInStore(was);
948 } else if (sscanf(str,"%d",i)>0 && i>0) {
949 _notify_fail("Das kann ich im Lager nicht finden.\n");
950 ob=FindInStore(i);
951 }
952 if (!ob) return 0;
953 write(ob->Name(WER)+":\n"+ob->long()+capitalize(ob->QueryPronoun())
954 +" kostet "+QueryBuyValue(ob,PL)+" Muenzen.\n");
955 return 1;
956}
957
958// benutzt von trading_price::QueryValue(object, int, object)
959static int ObjectCount(object ob)
960{
961 string tmp;
962
963 if (!objectp(ob)) return 0;
964 tmp = BLUE_NAME(ob);
965 if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/") tmp=ob->short()+tmp;
966 return ob_anz[tmp];
967}
968
969// benutzt von trading_price::QuerySellValue(object, object)
970static varargs int QueryValue(object ob, int value, object client)
971{
972 int new_value, mymoney;
973
974 if (!objectp(ob)) return 0;
975 if (Query("laden::compat")) {
976 new_value=(value>1000?1000:value);
977 mymoney = QueryProp(P_CURRENT_MONEY);
978 if (new_value>mymoney)
979 return (mymoney>0?mymoney:0);
980 else return new_value;
981 }
982 return ::QueryValue(ob, value, client);
983}
984
985void reset()
986{
987 mixed *keys;
988 int i;
989
990 trading_price::reset();
991
992 if (!mappingp(ob_anz))
993 return;
994 keys=m_indices(ob_anz);
995 for (i=sizeof(keys)-1;i>=0;i--) {
996 ob_anz[keys[i]]=ob_anz[keys[i]]*7/8;
997 if (!ob_anz[keys[i]])
998 m_delete(ob_anz,keys[i]);
999 }
1000}
1001
1002varargs int CheckFindRestrictions(object ob, mixed restr, closure qp) {
1003 return 0;
1004}
1005
1006int EvalWeapon(object ob, closure qp) {
1007 int wc,val;
1008
1009 wc=funcall(qp,P_WC);
1010 val=funcall(qp,P_EFFECTIVE_WC);
1011 if (val>wc) wc=val;
1012 return wc;
1013}
1014
1015varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
1016 int bestwc, mixed restr) {
1017 object bestob,ob;
1018 string otype;
1019 int wc,bestval,val,w,bestw;
1020 closure qp;
1021
1022 if (!stringp(storage) || !objectp(ob=find_object(storage))) return 0;
1023 if (!maxmon) maxmon=100000;
1024 if (!maxw) maxw=75000;
1025 if (!hands) hands=2;
1026 if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
1027 if (type && !pointerp(type) && !mappingp(type)) type=({type});
1028
1029 for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
1030 qp=symbol_function("QueryProp",ob);
1031 if (!otype=funcall(qp,P_WEAPON_TYPE)) continue;
1032 if (type && member(type,otype)<0) continue;
1033 wc=EvalWeapon(ob,qp);
1034 if (wc<bestwc) continue;
1035 if (funcall(qp,P_NR_HANDS)>hands) continue;
1036 w=funcall(qp,P_WEIGHT);
1037 if (w>maxw) continue;
1038 val=funcall(qp,P_VALUE);
1039 if (val>maxmon) continue;
1040 if (bestob && wc<=bestwc) {
1041 if (val>bestval) continue;
1042 else if (val==bestval && w>bestw) continue;
1043 }
1044 if (val>bestval && bestob && wc<=bestwc) continue;
1045 if (CheckFindRestrictions(ob,restr,qp)) continue;
1046 bestob=ob;
1047 bestwc=wc;
1048 bestval=val;
1049 bestw=w;
1050 }
1051 return bestob;
1052}
1053
1054int EvalArmour(object ob,closure qp) {
1055 int ac,val;
1056
1057 ac=funcall(qp,P_AC);
1058 val=funcall(qp,P_EFFECTIVE_AC);
1059 if (val>ac) ac=val;
1060 return ac;
1061}
1062
1063varargs mapping FindBestArmoursT(mixed type, int maxmon, int maxw,
1064 mapping bestac, mixed restr) {
1065 object ob;
1066 string otype;
1067 int ac,val,sum,w,wsum;
1068 mapping bestob,bestval,bestw;
1069 closure qp;
1070
1071 if (!stringp(storage) || !objectp(ob=find_object(storage))) return ([]);
1072 if (!maxmon) maxmon=100000;
1073 if (!maxw) maxw=75000;
1074 if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
1075 if (type && !pointerp(type) && !mappingp(type)) type=({type});
1076 if (!mappingp(bestac)) bestac=([]);
1077 bestob=([]);bestval=([]);bestw=([]);
1078
1079 for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
1080 qp=symbol_function("QueryProp",ob);
1081 if (!otype=funcall(qp,P_ARMOUR_TYPE)) continue;
1082 if (type && member(type,otype)<0) continue;
1083 ac=EvalArmour(ob,qp);
1084 if (ac<bestac[otype]) continue;
1085 w=funcall(qp,P_WEIGHT);
1086 if (wsum-bestw[otype]+w>maxw) continue;
1087 val=funcall(qp,P_VALUE);
1088 if (sum-bestval[otype]+val>maxmon) continue;
1089 if (bestob[otype] && ac<=bestac[otype]) {
1090 if (val>bestval[otype]) continue;
1091 else if (val==bestval[otype] && w>bestw[otype]) continue;
1092 }
1093 if (CheckFindRestrictions(ob,restr,qp)) continue;
1094 sum=sum-bestval[otype]+val;
1095 wsum=wsum-bestw[otype]+w;
1096 bestob[otype]=ob;
1097 bestac[otype]=ac;
1098 bestval[otype]=val;
1099 bestw[otype]=w;
1100 }
1101 return bestob;
1102}
1103
1104varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
1105 mapping bestac, mixed restr) {
1106 return m_values(FindBestArmoursT(type,maxmon,maxw,bestac,restr));
1107}