Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/room/shop.c b/std/room/shop.c
new file mode 100644
index 0000000..c429127
--- /dev/null
+++ b/std/room/shop.c
@@ -0,0 +1,1110 @@
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/trading_price";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/description.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <rooms.h>
+#include <language.h>
+#include <moving.h>
+#include <routingd.h>
+#include <bank.h>
+#include <combat.h>
+#include <input_to.h>
+#include <unit.h>
+#include <money.h>
+
+// TODO: pruefen, um die Variablen private sein koennen.
+
+// allgemein benoetige Variablen
+nosave string storage; // Filename des Stores in dem die Objekte lagern...
+nosave mapping ob_anz; // wie oft ist ein Objekt im Laden vorhanden?
+
+// Jetzt Variablen fuer staendig verfuegbare Objekte:
+nosave string *fixed_obj; // Liste der staendig verfuegbaren Objekte
+nosave mapping fixed_value; // Preise bei Sonderangeboten
+nosave mapping fixed_ids; // Ids
+
+varargs void AddFixedObject(string str, int val, string|string* ids)
+{
+ int i;
+
+ // Achtung, bei solchen Objekten muss die Blueprint initialisiert werden!
+ if (!str) return;
+ if (!val) val=str->QueryProp(P_VALUE);
+ if (!ids)
+ {
+ if (str->QueryProp("u_ids")) // units haben keine P_IDS
+ ids=str->QueryProp("u_ids")[0];
+ else
+ ids=str->QueryProp(P_IDS);
+ }
+ if (!pointerp(ids))
+ {
+ if (stringp(ids))
+ ids=({ids});
+ else
+ ids=({});
+ }
+
+ fixed_obj += ({str});
+ fixed_value[str] = val;
+ // Alle IDs entfernen, die Sonderzeichen enthalten. Die koennte ein Spieler
+ // bei "kaufe" ohnehin nicht eingeben.
+ ids -= regexp(ids, "[\b\n\r\t]");
+ foreach(string id : ids)
+ {
+ // Nur IDs aufnehmen, die keine Grossbuchstaben enthalten, da Spieler
+ // diese ebenfalls nicht eingeben koennte.
+ if ( lowerstring(id) == id )
+ fixed_ids[id]=str;
+ }
+}
+
+void RemoveFixedObject(string filename)
+{
+ if( !stringp(filename) || !sizeof(fixed_obj))
+ return;
+ if( member(fixed_obj, filename)==-1 )
+ return;
+
+ fixed_obj -= ({ filename });
+ m_delete(fixed_value, filename);
+
+ foreach(string id, string file : fixed_ids)
+ {
+ if ( file == filename )
+ m_delete(fixed_ids, id);
+ }
+}
+
+static string SetStorageRoom(string str)
+{
+ if (str && stringp(str)) return storage=str;
+ return 0;
+}
+
+string QueryStorageRoom()
+{ return storage; }
+
+protected void create()
+{
+ object router;
+
+ if (object_name(this_object()) == __FILE__[0..<3])
+ {
+ set_next_reset(-1);
+ return;
+ }
+
+ trading_price::create();
+
+ SetProp( P_NAME, "Haendler" );
+ SetProp( P_GENDER, MALE );
+ SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_SHOP );
+
+ AddCmd("zeige","list");
+ AddCmd(({"kauf","kaufe","bestell","bestelle"}),"buy");
+ AddCmd(({"verkauf","verkaufe","verk"}),"sell");
+ AddCmd(({"versetz","versetze"}),"force_sell");
+ AddCmd(({"schaetz","schaetze"}),"evaluate");
+ AddCmd(({"untersuche","unt"}), "show_obj");
+
+ SetTradingData(50000,300,3);
+
+ ob_anz=([]);
+ fixed_obj=({});fixed_value=([]);fixed_ids=([]);
+
+ AddFixedObject(BOERSE, 80,({ "boerse","geldboerse"}));
+
+ if (!clonep(ME) && objectp(router=find_object(ROUTER)))
+ router->RegisterTarget(TARGET_SHOP,object_name(ME));
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+// Legacy-Version von GetShopItems() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static mixed *GetList()
+{
+ object store = load_object(storage);
+ store->_register_shop(ME);
+
+ mixed *output=({});
+ if (!objectp(store))
+ return output;
+
+ mixed tmp = map(fixed_obj, #'load_object)+all_inventory(store);
+ mapping tmp2 = ([]);
+ mixed str;
+ string comp;
+ int i;
+ int s=1;
+
+ for (i=sizeof(tmp)-1 ; i>=0 ; i--)
+ {
+ str = ({ ({ sprintf("%-25.25s%7.7d",
+ (tmp[i]->short()||"???")[0..<3],
+ QueryBuyValue(tmp[i], PL)),
+ tmp[i] }) });
+ comp=str[0][0][0..25];
+ if (!tmp2[comp])
+ {
+ tmp2[comp] = s++;
+ output += str;
+ }
+ else output[tmp2[comp]-1][0] = str[0][0];
+ }
+ return output;
+}
+
+// Legacy-Version von PrintList() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static int DoList(string query_fun)
+{
+ mixed* output=GetList();
+ int si = sizeof(output);
+ if (!si)
+ {
+ write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+ return 1;
+ }
+ string out="";
+ int i;
+ int indent;
+ for (i=0; i<si; i++)
+ {
+ if (call_other(ME, query_fun, output[i][1]))
+ {
+ indent = !indent;
+ out += sprintf("%3d. %s", i+1, output[i][0]);
+ if (!indent)
+ out += "\n";
+ else out += " | ";
+ }
+ }
+ if (indent)
+ out+="\n";
+ PL->More(out);
+ return 1;
+}
+
+// Liefert eine Liste der Objekte zurueck, die gerade im Storage liegen,
+// pro Blueprint jeweils eins.
+protected object* GetShopItems()
+{
+ object store = load_object(storage);
+ store->_register_shop(ME);
+ object* output = ({});
+ object* all_items = all_inventory(store);
+
+ // Wir brauchen eine Liste, die von jeder Blueprint nur einen Clone
+ // enthaelt. Daher werden die Ladenamen der Objekte als Keys im Mapping
+ // <items> verwendet und jeweils der aktuelle Clone als Value zugeordnet.
+ mapping items = m_allocate(sizeof(all_items));
+ foreach(object ob: all_items)
+ {
+ items[load_name(ob)] = ob;
+ }
+ // Die Fixed Objects werden ans Ende angehaengt, damit in dem Fall, dass
+ // ein Clone eines solchen Objektes im Lager liegt, dieser zuerst verkauft
+ // wird und nicht immer wieder ein neuer erstellt wird.
+ return m_values(items) + map(fixed_obj, #'load_object);
+}
+
+#define LIST_LONG 1
+#define LIST_SHORT 0
+
+// Kuemmert sich um die Listenausgabe fuer den Befehl "zeige"
+varargs protected int PrintList(string filter_fun, int liststyle)
+{
+ // Alle Items im Lager holen. Wenn keine vorhanden, tut uns das leid.
+ object *items_in_store = GetShopItems();
+ if ( !sizeof(items_in_store) ) {
+ write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+ return 1;
+ }
+
+ // Das Listenformat ist von der Spielereingabe abhaengig. Wurde "lang"
+ // angefordert, geben wir einspaltig aus mit groesserer Spaltenbreite.
+ // Die Spaltenbreite wird dabei von dem Item mit dem laengsten Namen im
+ // gesamten Shop-Inventar bestimmt, damit nicht bei jeder Teilliste
+ // (Waffen, Ruestungen, Verschiedenes) unterschiedliche Breiten rauskommen.
+ //
+ // Der erste Parameter enthaelt die Katalognummer des Items, der zweite
+ // die Kurzbeschreibung, der dritte den Preis.
+ string listformat = "%3d. %-25.25s %6.6d";
+ if ( liststyle == LIST_LONG )
+ {
+ string* names = sort_array(
+ items_in_store->short(),
+ function int (string s1, string s2) {
+ return (sizeof(s1) < sizeof(s2));
+ });
+ // Wenn einspaltig ausgegeben wird, soll die Liste nicht beliebig breit
+ // werden. Daher wird die Short auf 65 Zeichen limitiert.
+ int len = 65;
+ if ( sizeof(names) )
+ len = min(len, sizeof(names[0]));
+ listformat = "%3d. %-"+len+"."+len+"s %6.6d";
+ }
+
+ string out="";
+ // Variablen, die innerhalb der Schleife benoetigt werden.
+ string kurz;
+ int indent, preis;
+ object item;
+ // Ueber die Liste laufen. <i> wird benoetigt, um die ausgegebene Liste
+ // konsistent numerieren zu koennen, damit kaufe <nr> funktioniert.
+ foreach(int i : sizeof(items_in_store))
+ {
+ item = items_in_store[i];
+ if ( call_other(ME, filter_fun, item) )
+ {
+ // Kurzbeschreibung und Preis ermitteln. Items ohne Short werden
+ // als "?" angezeigt.
+ kurz = (item->short() || "???")[0..<3];
+ preis = QueryBuyValue(item, PL);
+ // Beschreibung des Items anfuegen.
+ out += sprintf(listformat, i+1, kurz, preis);
+ indent = !indent;
+ // Wenn indent gesetzt ist, handelt es sich um das linke Item in der
+ // zweispaltigen Liste, dann fuegen wir einen Spaltentrenner an,
+ // ansonsten ist es das rechte, dann brechen wir um.
+ // Gilt natuerlich nur fuer kurze Listen.
+ out += ((indent && liststyle==LIST_SHORT)? " | " : "\n");
+ }
+ }
+ // Wenn die Liste eine ungerade Zahl Items enthaelt, ist in der letzten
+ // Zeile links ein Item aufgefuehrt, daher muss danach umbrochen werden.
+ // Gilt natuerlich nur fuer kurze Listen
+ if (indent && liststyle==LIST_SHORT)
+ out+="\n";
+
+ // Vor den Listen wird eine Info-Zeile ausgegeben, um gefilterte Listen
+ // kenntlich zu machen. Wird nach der Filterung des Inventars erzeugt,
+ // um eine leere Meldung ausgeben zu koennen, wenn nach Filterung nichts
+ // mehr uebrigbleibt.
+ string was;
+ switch(filter_fun)
+ {
+ case "IsArmour": was = "Ruestungen"; break;
+ case "IsWeapon": was = "Waffen"; break;
+ case "NoWeaponNoArmour":
+ was = (out==""?"sonstigen Waren":"sonstige Waren"); break;
+ default: was = "Waren"; break;
+ }
+ // <out> ist ein Leerstring, wenn keine Waren da sind, die dem Filterkri-
+ // terium entsprechen. Dann gibt's eine entsprechende Meldung.
+ if ( out == "" )
+ out = sprintf("Leider sind momentan keine %s im Angebot.\n", was);
+ else
+ out = sprintf("Folgende %s kannst Du hier kaufen:\n",was) + out;
+
+ PL->More(out);
+ return 1;
+}
+
+// Hilfsfunktionen zum Filtern des Ladeninventars fuer den "zeige"-Befehl
+static int AlwaysTrue(object ob)
+{ return 1; }
+
+static string IsWeapon(object ob)
+{ return ob->QueryProp(P_WEAPON_TYPE); }
+
+static string IsArmour(object ob)
+{ return ob->QueryProp(P_ARMOUR_TYPE); }
+
+static int NoWeaponNoArmour(object ob)
+{ return (!ob->QueryProp(P_WEAPON_TYPE) && !ob->QueryProp(P_ARMOUR_TYPE)); }
+
+
+// Diese Funktion ist oeffentlich, falls Magier abfragen wollen, ob ein laden
+// ein Objekt zerstoeren wuerde. Aber: Benutzung auf eigenes Risiko! Es wird
+// nicht garantiert, dass diese Funktion bzw. ihr Interface sich nicht
+// aendert.
+public int CheckForDestruct(object ob) // Pruefen, ob zerstoert werden soll
+{
+ string str;
+ /*
+ * P_NOBUY - Objekte auf jeden Fall zerstoeren
+ */
+ if(ob->QueryProp(P_NOBUY)) return 1;
+ /*
+ * Beschaedigte Objekte werden ebenfalls zerstoert
+ */
+ if(ob->QueryProp(P_DAMAGED)) return 1;
+ /*
+ * Ruestungen wenn sie a) eine DefendFunc definiert haben oder
+ * b) ueber der in KEEP_ARMOUR_CLASS definierten AC
+ * liegen (siehe /sys/combat.h)
+ */
+ if(str = IsArmour(ob))
+ {
+ if(objectp(ob->QueryProp(P_DEFEND_FUNC))) return 1;
+ if(ob->QueryProp(P_AC) >= KEEP_ARMOUR_CLASS[str]) return 1;
+ return 0;
+ }
+ /*
+ * Waffen wenn sie a) 1-haendig sind und eine WC > 120 besitzen oder
+ * b) 2-haendig sind und eine WC > 150 besitzen oder aber
+ * c) eine HitFunc definiert haben
+ */
+ if(str = IsWeapon(ob))
+ {
+ if(ob->QueryProp(P_NR_HANDS) > 1 && ob->QueryProp(P_WC) > 150) return 1;
+ if(ob->QueryProp(P_NR_HANDS) < 2 && ob->QueryProp(P_WC) > 120) return 1;
+ if(objectp(ob->QueryProp(P_HIT_FUNC))) return 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int list(string str)
+{
+ _notify_fail(
+ "Bitte 'zeige', 'zeige waffen', 'zeige ruestungen' oder\n"
+ "'zeige verschiedenes' eingeben. Wenn Du das Schluesselwort 'lang'\n"
+ "oder '-1' anhaengst, wird die Liste einspaltig ausgegeben.\n");
+
+ if (!stringp(str) || !sizeof(str) )
+ return PrintList("AlwaysTrue");
+ if ( str == "lang" || str == "-1" )
+ return PrintList("AlwaysTrue", LIST_LONG);
+
+ string *params = explode(str," ");
+ if (sizeof(params[0])<3)
+ return 0;
+
+ int liststyle = LIST_SHORT;
+ if ( sizeof(params)>1 && params[1] == "lang" )
+ liststyle = LIST_LONG;
+
+ str=params[0][0..2];
+ if (str=="waf")
+ return PrintList("IsWeapon", liststyle);
+ if (str=="ver")
+ return PrintList("NoWeaponNoArmour", liststyle);
+ if (str=="rue")
+ return PrintList("IsArmour", liststyle);
+ return 0;
+}
+/*
+static varargs int QueryBuyValue(mixed ob, object client)
+{
+ if (objectp(ob))
+ return trading_price::QueryBuyValue(ob, client);
+ return (fixed_value[ob]*QueryBuyFact(client))/100;
+}
+*/
+
+static varargs int QueryBuyValue(object ob, object client)
+{
+ int fprice = fixed_value[load_name(ob)];
+
+ return (fprice>0) ?
+ fprice*QueryBuyFact(client)/100 :
+ trading_price::QueryBuyValue(ob, client);
+}
+
+static void UpdateCounter(object ob, int num)
+{
+ string tmp;
+
+ if (!num || !objectp(ob)) return;
+ tmp=BLUE_NAME(ob);
+ if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/")
+ tmp=ob->short()+tmp;
+ ob_anz[tmp] += num;
+ if (ob_anz[tmp] <= 0)
+ m_delete(ob_anz,tmp);
+}
+
+protected object FindInStore(string|int x)
+{
+ object* list = GetShopItems();
+ if ( intp(x) && x>0 && x<=sizeof(list) ) {
+ return list[x-1];
+ }
+ if (stringp(x))
+ {
+ if ( fixed_ids[x] )
+ return load_object(fixed_ids[x]);
+ list = filter_objects(list, "id", x);
+ if ( sizeof(list) )
+ return list[0];
+ // Wenn nix im Store gefunden (das schliesst eigentlicht schon die BPs der
+ // fixen Objekte ein, aber nicht, wenn diese nicht konfiguriert sind. D.h.
+ // diese Pruefung ist fuer nicht-konfigurierte BPs), Liste der
+ // FixedObjects pruefen unde so die eventuell manuell in
+ // AddFixedObject() angegebene Liste von IDs beruecksichtigen.
+ else if ( fixed_ids[x] )
+ return load_object(fixed_ids[x]);
+ }
+ return 0;
+}
+
+static string buy_obj(mixed ob, int short)
+{ return 0; }
+
+private void really_buy(int val, object pl, object ob, int u_req)
+{
+ // Staendig verfuegbare Objekte (fixed_obj) sind daran erkennbar, dass sie
+ // nicht im Lager liegen. Daher hier einen Clone erstellen, der dann
+ // stattdessen rausgegeben wird.
+ if ( !present(ob, find_object(storage)) )
+ ob = clone_object(ob);
+
+ // In Unitobjekten U_REQ (wieder) setzen (wegen input_to (bei dem sich das
+ // Kommandoverb aendert und deswegen U_REQ geloescht wird), und wegen
+ // Kaufens von Fixed-Objekt-Unitobjekten (bei diesen muss U_REQ _nach_ dem
+ // Clonen im Clone gesetzt werden, nicht automagisch in der BP durch den
+ // Aufruf von id() weiter vorher).
+ if (u_req>0)
+ {
+ // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+ // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+ // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+ // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+ // werden...
+ ob->QueryProp(U_REQ);
+ ob->SetProp(U_REQ, u_req);
+ }
+
+ pl->AddMoney(-val);
+ _add_money(val);
+
+ if (ob->move(pl,M_GET) != MOVE_OK) // Kann der Spieler das Objekt tragen?
+ {
+ write(break_string("Du kannst "+ob->name(WEN,1)+" nicht mehr tragen. "
+ "Ich lege "+ob->QueryPronoun(WEN)+" hier auf den Boden.",78,
+ Name(WER)+" sagt: "));
+ ob->move(ME,M_NOCHECK); // Nein :-)
+ }
+ else
+ {
+ // Falls das Objekt sich vereinigt hat, muss jetzt wieder U_REQ
+ // restauriert werden.
+ ob->SetProp(U_REQ, u_req);
+ write(break_string("Du kaufst "+ob->name(WEN,1)+".", 78));
+ }
+
+ say(break_string(PL->Name(WER)+" kauft "+ob->name(WEN)+".",78), ({PL}));
+ UpdateCounter(ob,-1);
+}
+
+static void ask_buy(string str, int val, object pl, object ob, int u_req)
+{
+ _notify_fail(break_string("Gut, Du kannst es Dir ja jederzeit "
+ "noch anders ueberlegen.",78,Name(WER)+" sagt: "));
+
+ if(!str || !stringp(str) || str == "nein" || str == "n")
+ {
+ return;
+ }
+ if(str != "ja" && str != "j")
+ {
+ return;
+ }
+ really_buy(val, pl, ob, u_req);
+}
+
+static int buy(string str)
+{
+ int i, val, par, dex;
+ mixed ob, res;
+ string dummy;
+
+ if (!str) {
+ write("Was willst Du kaufen?\n");
+ return 1;
+ }
+
+ if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+ write("Es ist niemand da, der Dich bedienen koennte.\n");
+ return 1;
+ }
+
+ _notify_fail(break_string("Das kann ich in meinem Lager nicht finden.",78,
+ Name(WER)+" sagt: "));
+
+ // Um auch Teile von Unit-Stacks kaufen zu koennen, z.B. "kaufe 5 phiolen",
+ // darf hier zusaetzlich <dummy> nur ein Leerstring sein, sonst verzweigt
+ // die Syntaxpruefung hierhin und es wird das 5. Item der Liste gekauft.
+ if (sscanf(str,"%d%s",i,dummy)>0 && i>0 && !sizeof(dummy)) {
+ ob=FindInStore(i);
+ }
+ else ob=FindInStore(str);
+
+ if (!ob) return 0;
+
+ if (str = buy_obj(ob,0))
+ {
+ write(break_string(str,78,Name(WER)+" sagt: "));
+ return 1;
+ }
+
+ val = QueryBuyValue(ob,PL);
+
+ if (PL->QueryMoney() < val)
+ {
+ write(break_string(capitalize(ob->QueryPronoun(WER))+" wuerde "+val+
+ " Muenzen kosten, und Du hast nur "+PL->QueryMoney()+".",78,
+ Name(WER)+" bedauert: "));
+ return 1;
+ }
+
+ // Anzahl der angeforderten Einheiten vor dem Bewegen zwischenspeichern.
+ // Weil dabei im Fall von Units eine Vereinigung mit bereits im Inventar
+ // befindlichen Einheiten stattfindet, muss das ggf. nach Bewegen
+ // zurueckgesetzt werden.
+ int u_req = ob->QueryProp(U_REQ);
+
+ if ((res = ob->QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+ (res = call_other("/std/restriction_checker",
+ "check_restrictions",PL,res)) &&
+ stringp(res))
+ {
+ _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+ "verwenden. Grund: "+res+"Moechtest Du "+ob->QueryPronoun(WEN)+
+ " dennoch kaufen?",78,Name(WER)+" sagt: "));
+
+ input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+ return 0;
+ }
+
+ par = (int)ob->QueryProp(P_PARRY);
+ dex = (int)PL->QueryAttribute(A_DEX);
+
+ if ((((par < PARRY_ONLY) && ((dex + 8) * 10) < ob->QueryProp(P_WC)) ||
+ ((par > PARRY_NOT) && ((dex + 5) * 2) < ob->QueryProp(P_AC))) &&
+ VALID_WEAPON_TYPE(ob))
+ {
+ _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+ "zuecken, da Deine Geschicklichkeit dafuer nicht ausreicht. Moechtest "
+ "Du "+ob->QueryPronoun(WEN)+" dennoch kaufen?",78,
+ Name(WER)+" sagt: "));
+
+ input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+ return 0;
+ }
+
+ really_buy(val, PL, ob, u_req);
+
+ return 1;
+}
+
+private void give_money(int value)
+// Geld gutschreiben...
+{
+ if (!value) return;
+ write(break_string(Name(WER, 1)+" zahlt Dir "+value+" Goldstueck"
+ +(value==1?".":"e."), 78));
+ if ((PL->AddMoney(value))<=0) {
+ object mon;
+
+ write("Du kannst das Geld nicht mehr tragen!\n");
+ mon=clone_object(GELD);
+ mon->SetProp(P_AMOUNT,value);
+ mon->move(ME,M_MOVE_ALL|M_NOCHECK);
+ }
+}
+
+static int make_to_money(object ob, int value)
+// Interne Funktion, die ob versucht in das Lager zu ueberfuehren und das
+// Geld das dabei fuer den Spieler abfaellt zurueckliefert.
+{
+ string str;
+ int ret;
+
+ if (!objectp(ob) || environment(ob)==find_object(storage)) {
+ write(break_string(Name(WER, 1)+" wundert sich ueber Dich.", 78));
+ return 0;
+ }
+ if (value>QueryProp(P_CURRENT_MONEY)) {
+ write(break_string("Ich hab das Geld leider nicht mehr.", 78,
+ Name(WER, 1)+" sagt: "));
+ return 0;
+ }
+ // U_REQ merken, falls sich Objekte vereinigen. Sonst stimmt nicht nur der
+ // Name, sondern es werden ggf. auch die falsche Anzahl zerstoert.
+ //TOOO: Oder Units entsorgen und als Feature deklarieren?
+ int req = ob->QueryProp(U_REQ);
+ if (CheckForDestruct(ob) > 0) // soll ob zerstoert werden?
+ {
+ ret = ob->move(storage,M_PUT|M_GET);
+ // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+ // Einheiten restauriert werden.
+ // Problem: falls das verkaufte Objekt Units hat, beschaedigt ist und sich
+ // vereinigt hat, sind jetzt leider alle Einheiten im Lager beschaedigt.
+ // Das ist unschoen - aber mir jetzt zuviel AUfwand, das korrekt zu bauen,
+ // weil es nur sehr selten vorkommt. (Hint: separater Muellraum)
+ ob->SetProp(U_REQ, req);
+ if (ret > 0) // Sonst werden auch Sachen zerstoert, die man nicht
+ { // weglegen kann
+ say(break_string(PL->Name()+" verkauft "+ob->name(WEN)+".", 78));
+ if(ob->QueryProp(P_DAMAGED)) // Andere Meldung bei beschaedigten
+ { // Objekten ...
+ write(break_string(Name(WER,1)+" findet zwar keinen Gefallen an "
+ +ob->name(WEM,1)+", nimmt "+ob->QueryPronoun(WEN)+" Dir zuliebe "
+ "aber trotzdem.",78));
+ }
+ else
+ {
+ write(break_string(Name(WER, 1)+" findet Gefallen an "
+ +ob->name(WEM, 1) + " und legt "+ob->QueryPronoun(WEN)
+ +" zu "+(QueryProp(P_GENDER)==FEMALE?"ihren":"seinen")
+ +" Privatsachen.", 78));
+ }
+ /* Er zahlt Dir "+value+" Muenze"+(value==1?"":"n")+" dafuer.", 78)); */
+ _add_money(-value);
+ _add_money(value*QueryProp(P_SHOP_PERCENT_LEFT)/100); // Wegen Zerstoerung des Objektes
+ UpdateCounter(ob,1);
+ ob->remove(1);
+ return value;
+ }
+ else if (ret == ME_CANT_BE_DROPPED) {
+ if ((str=ob->QueryProp(P_NODROP)) && stringp(str)) {
+ write(str);
+ return 0;
+ }
+ write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+ return 0;
+ }
+ else
+ write(break_string(ob->Name(WER)+" interessiert mich nicht.", 78,
+ Name(WER, 1)+" sagt: "));
+ }
+ else if ((ret=(ob->move(storage,M_PUT|M_GET)))>0)
+ {
+ // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+ // Einheiten restauriert werden.
+ ob->SetProp(U_REQ, req);
+ say(break_string(PL->Name(WER)+" verkauft "+ob->name(WEN)+".", 78));
+ _add_money(-value);
+ UpdateCounter(ob,1);
+ return value;
+ }
+ else if (ret == ME_CANT_BE_DROPPED) {
+ if ((str=ob->QueryProp(P_NODROP)) && stringp(str))
+ write(str);
+ else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+ }
+ else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+ return 0;
+}
+
+static void ask_sell(string str, object ob, int val, int u_req)
+// Wenn ein einzelnen Stueck unter Wert verkauft werden soll, wird nachgefragt
+// u_req ist bei Unitobjekten die Anzahl an zu verkaufenden Einheiten. Bei
+// normalen Objekten ist u_req 0.
+{
+ str=lower_case(str||"");
+ if (str=="ja"||str=="j")
+ {
+ // In Unitobjekten U_REQ (wieder) setzen.
+ if (u_req>0)
+ {
+ // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+ // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+ // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+ // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+ // werden...
+ ob->QueryProp(U_REQ);
+ ob->SetProp(U_REQ, u_req);
+ }
+ give_money(make_to_money(ob,val));
+ }
+ else
+ write(break_string("Ok, dann behalts!", 78,
+ Name(WER, 1)+" sagt: "));
+}
+
+static string sell_obj(object ob, int short)
+// Ist der Haendler bereit ob zu kaufen? wenn nein, Rueckgabe einer Meldung,
+// die der Haendler sagen soll.
+{ mixed nosell;
+
+ if (BLUE_NAME(ob)==GELD)
+ return "Das waere ja wohl Unsinn, oder ...?";
+ if (nosell=ob->QueryProp(P_NOSELL))
+ {
+ if (stringp(nosell))
+ return nosell;
+ return ("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!");
+ }
+ if (ob->QueryProp(P_CURSED))
+ return ob->Name(WER,1)
+ +" ist mir irgendwie ungeheuer! Das kannst Du nicht verkaufen!";
+ // man sollte keine COntainer mit Inhalt verkaufen koennen, ggf. kauft sie
+ // dann ein anderer Spieler.
+ if (first_inventory(ob))
+ {
+ return ob->Name(WER, 1) + " ist nicht leer!";
+ }
+ return 0;
+}
+
+static varargs int sell(string str, int f)
+{
+ int i, val, oval, tmp;
+ object *obs;
+
+ if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER),ME)) {
+ write("Es ist niemand da, der Dich bedienen koennte.\n");
+ return 1;
+ }
+
+ if (!str) {
+ notify_fail("Was moechtest Du denn verkaufen?\n");
+ return 0;
+ }
+
+ /* Ergebnis von find_obs() sollte unifiziert werden, damit ein mehrfach
+ gefundenes Objekt nicht mehrfach versucht wird zu verkaufen.
+ Beispiel: Objekt hat P_NOBUY gesetzt und mehrere IDs gesetzt. Wenn
+ ein Spieler es mit "verkaufe ID1 und ID2" versucht zu verkaufen,
+ wuerde das einen Bug ausloesen. Derselbe Bug entsteht, wenn man mit
+ "verkaufe ID1 und ID1" verkauft. */
+ obs = PL->find_obs(str, PUT_GET_DROP);
+ /* Erst im Inventar schauen, dann im Environment. find_obs() ohne 2.
+ Parameter macht das standardmaessig andersherum.
+ TODO: Aenderung ueberpruefen, sobald das neue put_and_get.c
+ eingebaut wurde. */
+ if ( !sizeof(obs) )
+ obs = PL->find_obs(str, PUT_GET_TAKE) || ({});
+ obs = m_indices(mkmapping(obs));
+ if (!i=sizeof(obs)) {
+ notify_fail("Was moechtest Du denn verkaufen?\n");
+ return 0;
+ }
+ call_other(storage, "_register_shop", ME);
+ if (i==1) {
+ if (str=sell_obj(obs[0], 0)) {
+ write(break_string(str, 78, Name(WER, 1)+" sagt: "));
+ return 1;
+ }
+ if ((oval=obs[0]->QueryProp(P_VALUE))<=0) {
+ write(break_string(obs[0]->Name(WER)
+ +(obs[0]->QueryProp(P_PLURAL) ? " haben" : " hat")
+ +" keinen materiellen Wert, tut mir leid.", 78,
+ Name(WER, 1)+" sagt: "));
+ return 1;
+ }
+ val=QuerySellValue(obs[0], PL);
+ if (!val) {
+ write(break_string("Ich bin absolut pleite. Tut mir aufrichtig leid.",
+ 78, Name(WER, 1)+" sagt: "));
+ return 1;
+ }
+ if (val==oval || f) {
+ give_money(make_to_money(obs[0], val));
+ return 1;
+ }
+ if (str=obs[0]->QueryProp(P_NODROP)) {
+ if (stringp(str))
+ write(str);
+ else write(break_string("Du kannst "+obs[0]->name(WEN,1)
+ +" nicht verkaufen!", 78));
+ return 1;
+ }
+
+ if (obs[0]->QueryProp(P_DAMAGED)) // Bei beschaedigten Objekten gibt
+ { // es auch hier eine andere Meldung
+ write(break_string("Da "+obs[0]->name(WER)+" beschaedigt "
+ +(obs[0]->QueryProp(P_PLURAL)?"sind":"ist")+", kann ich Dir "
+ "nur "+val+" Muenze"+(val == 1?"":"n")+" dafuer bieten. Und "
+ "damit mache ich noch Verlust! Nimmst Du mein Angebot an? "
+ "(ja/nein)",78,Name(WER,1)+" sagt: "));
+ }
+ else // Default
+ {
+ write(break_string(Name(WER, 1)+" sagt: "
+ +"Nach der aktuellen Marktlage kann ich Dir dafuer nur "
+ +val+" Muenze"+(val==1?"":"n")+" bieten, obwohl "
+ +obs[0]->name(WER)+" eigentlich "+oval+" Muenze"
+ +(oval==1?"":"n")+" wert waere. Willst Du "
+ +(QueryProp(P_PLURAL) ? "sie" : "es")
+ +" mir dafuer ueberlassen?", 78));
+ }
+ // in ask_sell() gibt es das query_verb() nicht mehr, weswegen U_REQ in
+ // Unitobjekten zurueckgesetzt wird. Damit geht die info verloren,
+ // wieviele Objekte der Spieler angegeben hat. Diese muss gerettet und
+ // via ask_sell() in make_to_money() ankommen. In normalen Objekten ist
+ // U_REQ 0.
+ input_to("ask_sell",INPUT_PROMPT, "(ja/nein) ",obs[0], val,
+ (obs[0])->QueryProp(U_REQ) );
+ return 1;
+ }
+ for (--i; i>=0 && get_eval_cost()>50000; i--) {
+ if (oval=obs[i]->QueryProp(P_VALUE)) {
+ if (obs[i]->QueryProp(P_KEEP_ON_SELL)==getuid(PL)
+ || obs[i]->QueryProp(P_WORN) || obs[i]->QueryProp(P_WIELDED))
+ write(break_string(obs[i]->Name(WER)+": Du behaeltst "
+ +obs[i]->name(WEN)+".", 78));
+ else if (str=sell_obj(obs[i], 1))
+ write(break_string(obs[i]->Name(WER)+": "+str, 78));
+ else {
+ tmp=QuerySellValue(obs[i], PL);
+ if (!tmp) {
+ write(break_string(
+ "Ich bin absolut pleite. Tut mir aufrichtig leid.", 78,
+ Name(WER, 1)+" sagt: "));
+ break;
+ }
+ else if (!f && tmp*10<oval)
+ write(break_string(obs[i]->Name(WER)+": "+Name(WER, 1)
+ +" bietet Dir aber nur "+tmp+" Goldstueck"
+ +(tmp>1 ? "e" : "")+" dafuer.", 78));
+ else {
+ str=(obs[i]->Name(WER));
+ if (tmp=make_to_money(obs[i], tmp)) {
+ write(break_string(str+": "+Name(WER, 1)
+ +" gibt Dir dafuer "+tmp+" Goldstueck"
+ +(tmp==1?".":"e."), 78));
+ val+=tmp;
+ }
+ }
+ }
+ }
+ }
+ if (!val)
+ write(break_string("Hmmm, Du hast aber nicht besonders viel zu bieten...",
+ 78, Name(WER)+" sagt: "));
+ else give_money(val);
+ return 1;
+}
+
+static int force_sell(string str)
+{ return sell(str, 1); }
+
+static int evaluate(string str)
+{
+ object ob;
+ int val,rval;
+
+ if (!str) return 0;
+ if(stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+ write("Es ist niemand da, der Dich bedienen koennte.\n");
+ return 1;
+ }
+
+ ob=present(str, ME);
+ if (!ob) ob=deep_present(str,PL);
+ if (!ob) {
+ write("Hm? "+capitalize(str)+"? Wovon redest Du?\n");
+ return 1;
+ }
+ if (living(ob)) {
+ _notify_fail("Nanu, seit wann werden hier Lebewesen verkauft?\n");
+ return 0;
+ }
+ if (str=sell_obj(ob, 0)) {
+ write(break_string(str, 78, Name(WER)+" sagt: "));
+ return 1;
+ }
+ rval=ob->QueryProp(P_VALUE);
+ if (rval) {
+ val=QuerySellValue(ob, PL);
+ if (rval==val) {
+ tell_object(this_player(),break_string(
+ "Naja, ich denke, " +val+ " Muenze"
+ + (val==1 ? "" : "n")
+ + " waere"+(ob->QueryProp(P_AMOUNT)>1?"n ":" ")
+ + (ob->QueryPronoun(WER))+" schon wert.\n",78));
+ }
+ else if (val) {
+ tell_object(this_player(),break_string(
+ "Oh, nach der aktuellen Marktlage kann ich nur "+val+" Muenze"+
+ (val==1?"":"n")+" bezahlen, obwohl "
+ + (QueryProp(P_PLURAL) ? "sie" : "es")
+ + " eigentlich "+rval
+ + " Muenze"+(rval==1?"":"n")+" wert ist.\n",78));
+ }
+ else write("Ich bin vollkommen pleite. Tut mir leid.\n");
+ }
+ else write("Das ist voellig wertlos.\n");
+ return 1;
+}
+
+static int show_obj(string str)
+{
+ int i;
+ string was;
+ mixed ob;
+
+ if (!str) return 0;
+ if (sscanf(str,"%s im laden",was)>0 || sscanf(str,"%s in laden",was)>0) {
+ _notify_fail("Das kann ich im Lager nicht finden.\n");
+ ob=FindInStore(was);
+ } else if (sscanf(str,"%d",i)>0 && i>0) {
+ _notify_fail("Das kann ich im Lager nicht finden.\n");
+ ob=FindInStore(i);
+ }
+ if (!ob) return 0;
+ write(ob->Name(WER)+":\n"+ob->long()+capitalize(ob->QueryPronoun())
+ +" kostet "+QueryBuyValue(ob,PL)+" Muenzen.\n");
+ return 1;
+}
+
+// benutzt von trading_price::QueryValue(object, int, object)
+static int ObjectCount(object ob)
+{
+ string tmp;
+
+ if (!objectp(ob)) return 0;
+ tmp = BLUE_NAME(ob);
+ if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/") tmp=ob->short()+tmp;
+ return ob_anz[tmp];
+}
+
+// benutzt von trading_price::QuerySellValue(object, object)
+static varargs int QueryValue(object ob, int value, object client)
+{
+ int new_value, mymoney;
+
+ if (!objectp(ob)) return 0;
+ if (Query("laden::compat")) {
+ new_value=(value>1000?1000:value);
+ mymoney = QueryProp(P_CURRENT_MONEY);
+ if (new_value>mymoney)
+ return (mymoney>0?mymoney:0);
+ else return new_value;
+ }
+ return ::QueryValue(ob, value, client);
+}
+
+void reset()
+{
+ mixed *keys;
+ int i;
+
+ trading_price::reset();
+
+ if (!mappingp(ob_anz))
+ return;
+ keys=m_indices(ob_anz);
+ for (i=sizeof(keys)-1;i>=0;i--) {
+ ob_anz[keys[i]]=ob_anz[keys[i]]*7/8;
+ if (!ob_anz[keys[i]])
+ m_delete(ob_anz,keys[i]);
+ }
+}
+
+varargs int CheckFindRestrictions(object ob, mixed restr, closure qp) {
+ return 0;
+}
+
+int EvalWeapon(object ob, closure qp) {
+ int wc,val;
+
+ wc=funcall(qp,P_WC);
+ val=funcall(qp,P_EFFECTIVE_WC);
+ if (val>wc) wc=val;
+ return wc;
+}
+
+varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
+ int bestwc, mixed restr) {
+ object bestob,ob;
+ string otype;
+ int wc,bestval,val,w,bestw;
+ closure qp;
+
+ if (!stringp(storage) || !objectp(ob=find_object(storage))) return 0;
+ if (!maxmon) maxmon=100000;
+ if (!maxw) maxw=75000;
+ if (!hands) hands=2;
+ if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+ if (type && !pointerp(type) && !mappingp(type)) type=({type});
+
+ for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+ qp=symbol_function("QueryProp",ob);
+ if (!otype=funcall(qp,P_WEAPON_TYPE)) continue;
+ if (type && member(type,otype)<0) continue;
+ wc=EvalWeapon(ob,qp);
+ if (wc<bestwc) continue;
+ if (funcall(qp,P_NR_HANDS)>hands) continue;
+ w=funcall(qp,P_WEIGHT);
+ if (w>maxw) continue;
+ val=funcall(qp,P_VALUE);
+ if (val>maxmon) continue;
+ if (bestob && wc<=bestwc) {
+ if (val>bestval) continue;
+ else if (val==bestval && w>bestw) continue;
+ }
+ if (val>bestval && bestob && wc<=bestwc) continue;
+ if (CheckFindRestrictions(ob,restr,qp)) continue;
+ bestob=ob;
+ bestwc=wc;
+ bestval=val;
+ bestw=w;
+ }
+ return bestob;
+}
+
+int EvalArmour(object ob,closure qp) {
+ int ac,val;
+
+ ac=funcall(qp,P_AC);
+ val=funcall(qp,P_EFFECTIVE_AC);
+ if (val>ac) ac=val;
+ return ac;
+}
+
+varargs mapping FindBestArmoursT(mixed type, int maxmon, int maxw,
+ mapping bestac, mixed restr) {
+ object ob;
+ string otype;
+ int ac,val,sum,w,wsum;
+ mapping bestob,bestval,bestw;
+ closure qp;
+
+ if (!stringp(storage) || !objectp(ob=find_object(storage))) return ([]);
+ if (!maxmon) maxmon=100000;
+ if (!maxw) maxw=75000;
+ if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+ if (type && !pointerp(type) && !mappingp(type)) type=({type});
+ if (!mappingp(bestac)) bestac=([]);
+ bestob=([]);bestval=([]);bestw=([]);
+
+ for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+ qp=symbol_function("QueryProp",ob);
+ if (!otype=funcall(qp,P_ARMOUR_TYPE)) continue;
+ if (type && member(type,otype)<0) continue;
+ ac=EvalArmour(ob,qp);
+ if (ac<bestac[otype]) continue;
+ w=funcall(qp,P_WEIGHT);
+ if (wsum-bestw[otype]+w>maxw) continue;
+ val=funcall(qp,P_VALUE);
+ if (sum-bestval[otype]+val>maxmon) continue;
+ if (bestob[otype] && ac<=bestac[otype]) {
+ if (val>bestval[otype]) continue;
+ else if (val==bestval[otype] && w>bestw[otype]) continue;
+ }
+ if (CheckFindRestrictions(ob,restr,qp)) continue;
+ sum=sum-bestval[otype]+val;
+ wsum=wsum-bestw[otype]+w;
+ bestob[otype]=ob;
+ bestac[otype]=ac;
+ bestval[otype]=val;
+ bestw[otype]=w;
+ }
+ return bestob;
+}
+
+varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
+ mapping bestac, mixed restr) {
+ return m_values(FindBestArmoursT(type,maxmon,maxw,bestac,restr));
+}