| #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)); |
| } |