diff --git a/data/dfndata/items/shoplist.dfn b/data/dfndata/items/shoplist.dfn index 188bebb33..88adb7e76 100644 --- a/data/dfndata/items/shoplist.dfn +++ b/data/dfndata/items/shoplist.dfn @@ -407,6 +407,7 @@ RSHOPITEM=0x097e RSHOPITEM=0x098c RSHOPITEM=0x103c RSHOPITEM=0x09f2 +SELLITEM=playervendordeed SELLITEM=0x099f SELLITEM=0x099b SELLITEM=0x09c7 @@ -1550,6 +1551,7 @@ RSHOPITEM=0x1f98 RSHOPITEM=0x09c8 RSHOPITEM=0x09c4 RSHOPITEM=0x098d +SELLITEM=playervendordeed SELLITEM=0x097b SELLITEM=0x09bb SELLITEM=0x09f2 diff --git a/data/dictionaries/dictionary.CSY b/data/dictionaries/dictionary.CSY index 01293a24f..3984ccbf3 100644 --- a/data/dictionaries/dictionary.CSY +++ b/data/dictionaries/dictionary.CSY @@ -5731,5 +5731,97 @@ 30003=Šampion padl! 30004=Byl jsi odměněn Svitkem moci! 30005=Energie svatyně ubývá, protože nejsou přítomni žádní vyzyvatelé... +// [ 40000 - 40200 ] Hráč Prodejce +40000=%s si již nemůže dovolit údržbu a byl propuštěn. +40001=Nelze vytvořit prodejní listinu. Propuštění prodejce bylo zrušeno. +40002=Vrácené předměty prodejce - banka: %i, pozemek: %i. +40003=Vrácené předměty prodejce - balíček: %i, banka: %i, pozemek: %i. +40004=Vráceno %i zlata do vašeho balíčku. +40005=Vráceno %i zlata do vaší bankovní schránky. +40006=V lokaci prodejce bylo odloženo %i zlata. +40007=Listina prodejce byla umístěna do vašeho balíčku. +40008=Listina prodejce byla umístěna do vaší bankovní schránky. +40009=Listina prodejce byla odložena do lokace prodejce. +40010=%s si nemohl dovolit údržbu a byl propuštěn. +40011=Na prodejcově lokaci bylo odloženo %i prodejních předmětů. +40012=Tento prodejce nemá žádné zlato k vyzvednutí. +40013=Vaše bankovní schránka momentálně nemůže přijímat platby od prodejce. +40014=Nelze vytvořit bankovní šek na prodejní zlato. +40015=Ve vaší bankovní schránce bylo shromážděno %i zlata jako bankovní šek. +40016=Nelze vložit prodejní zlato do vaší bankovní schránky. +40017=Ve vaší bankovní schránce bylo shromážděno %i zlata. +40018=Neplatná částka vkladu. +40019=Nemáte batoh. +40020=Ve vašem batohu není dostatek zlata. +40021=Bankovní účet prodejce je plný. +40022=Nelze odebrat zlato z vašeho batohu. Vklad zrušen. +40023=Na bankovní účet dodavatele vloženo %i zlata. +40024=Banka dodavatele dosáhla maximálního zůstatku. +40025=Bankovní účet dodavatele je prázdný. +40026=Přesunuto %i zlata z bankovního účtu dodavatele do rezervovaného zlata. +40027=Nelze vytvořit smlouvu s dodavatelem. Zrušení zrušeno. +40028=Nepracuji pro vás! +40029=Zobrazit zboží +40030=Přizpůsobit +40031=Sbírat zlato +40032=Vložit zlato +40033=Vyprázdnit účet +40034=Zavřít +40035=Počet dnů, které mohu pracovat: +40036=Hodin, které mohu pracovat: +40037=Zlato držené pro vás: +40038=Zlato držené na mém účtu: +40039=Údržba dodavatele: +40040=Můj poplatek za období je: +40041=Hodiny účtovaného období: +40042=Celkový poplatek za položku: +40043=Neomezeně +40044=Zakázáno +40045=HLAVNÍ +40046=PŘIZPŮSOBENÍ DODAVATELEM MENU +40047=VLASY +40048=Přejmenovat prodejce +40049=Přejmenovat obchod +40050=Zadejte nový název prodejce. +40051=Zadejte nový název obchodu. +40052=Zadejte množství zlata, které chcete vložit. +40053=Toto není platný hráčský prodejce. +40054=Váš prodejce je nyní pojmenován: %s +40055=Název obchodu je příliš dlouhý. Maximálně 40 znaků. +40056=Název vašeho obchodu je nyní: %s +40057=Musíte zadat platné množství zlata. +40058=Krátké vousy a knír +40059=Z tohoto domu nemůžete nakupovat, protože vám byl do tohoto podniku zakázán vstup. +40068=Dlouhé vousy a Knír +40069=Kozí bradka +40071=Odstranit ochlupení +40072=Barvení vlasů +40073=VOUSY +40074=Odstranit vousy +40075=Barvení vousů +40076=Změnit na muže +40077=Změnit na ženu +40078=Změnit na člověka +40079=Změnit na elfa +40080=Změnit oblečení +40081=ZAVŘÍT +40082=BARVA VLASŮ +40083=BARVA VOUSŮ +40084=Vlasy - %s +40085=Vousy - %s +40086=NABÍDKA OBLEČENÍ PRODEJCE +40087=Barva +40088=Odstranit +40089=KATEGORIE BAREV +40090=HOTOVO +40091=OBLEČENÍ BARVA +40100=Speciální fialová +40101=Speciální červená +40102=Speciální oranžová +40103=Speciální žlutá +40104=Speciální limetková +40105=Speciální tmavě limetková +40106=Speciální ledově modrá +40107=Speciální ledově bílá } EOF diff --git a/data/dictionaries/dictionary.ENG b/data/dictionaries/dictionary.ENG index 398ae6b41..691459976 100644 --- a/data/dictionaries/dictionary.ENG +++ b/data/dictionaries/dictionary.ENG @@ -5731,5 +5731,97 @@ 30003=The Champion has fallen! 30004=You have been rewarded with a Scroll of Power! 30005=The shrine's energy wanes with no challengers present... +// [ 40000 - 40200 ] Player Vendor +40000=%s can no longer afford upkeep and has been dismissed. +40001=Unable to create vendor deed. Vendor dismissal aborted. +40002=Vendor items returned - bank: %i, ground: %i. +40003=Vendor items returned - pack: %i, bank: %i, ground: %i. +40004=Returned %i gold to your pack. +40005=Returned %i gold to your bank box. +40006=Dropped %i gold at vendor location. +40007=Placed vendor deed in your pack. +40008=Placed vendor deed in your bank box. +40009=Dropped vendor deed at vendor location. +40010=%s could not afford upkeep and was dismissed. +40011=Dropped %i vendor item(s) at vendor location. +40012=This vendor has no held gold to collect. +40013=Your bank box cannot receive vendor payouts right now. +40014=Unable to create a bank check for vendor gold. +40015=Collected %i gold as a bank check in your bank box. +40016=Unable to deposit vendor gold into your bank box. +40017=Collected %i gold to your bank box. +40018=Invalid deposit amount. +40019=You have no backpack. +40020=You do not have enough gold in your pack. +40021=Vendor bank account is full. +40022=Unable to remove gold from your pack. Deposit cancelled. +40023=Deposited %i gold into vendor bank account. +40024=Vendor bank reached its maximum balance. +40025=Vendor bank account is empty. +40026=Moved %i gold from vendor bank account to held gold. +40027=Unable to create vendor deed. Dismiss cancelled. +40028=I don't work for you! +40029=See goods +40030=Customize +40031=Collect Gold +40032=Deposit Gold +40033=Flush Bank +40034=Dismiss +40035=Amount of days I can work: +40036=Hours I can work: +40037=Gold held for you: +40038=Gold held in my account: +40039=Vendor upkeep: +40040=My charge per period is: +40041=Charge period hours: +40042=Item fee total: +40043=Unlimited +40044=Disabled +40045=MAIN +40046=VENDOR CUSTOMIZATION MENU +40047=HAIR +40048=Rename Vendor +40049=Rename Shop +40050=Enter a new vendor name. +40051=Enter a new shop name. +40052=Enter the amount of gold you wish to deposit. +40053=That is not a valid player vendor. +40054=Your vendor is now named: %s +40055=That shop name is too long. Maximum 40 chars. +40056=Your shop title is now: %s +40057=You must enter a valid gold amount. +40058=Short Beard and Mustache +40059=You can't shop from this home as you have been banned from this establishment. +40068=Long Beard and Mustache +40069=Goatee +40071=Remove Hair +40072=Color Hair +40073=BEARD +40074=Remove Beard +40075=Color Beard +40076=Change to Male +40077=Change to Female +40078=Change to Human +40079=Change to Elf +40080=Change Clothing +40081=CLOSE +40082=HAIR COLOR +40083=BEARD COLOR +40084=Hair - %s +40085=Beard - %s +40086=VENDOR CLOTHING MENU +40087=Color +40088=Remove +40089=COLOR CATEGORY +40090=DONE +40091=CLOTHING COLOR +40100=Special Purple +40101=Special Red +40102=Special Orange +40103=Special Yellow +40104=Special Lime +40105=Special Dark Lime +40106=Special Ice Blue +40107=Special Ice White } EOF diff --git a/data/dictionaries/dictionary.FRE b/data/dictionaries/dictionary.FRE index 63a2382a3..9a5144853 100644 --- a/data/dictionaries/dictionary.FRE +++ b/data/dictionaries/dictionary.FRE @@ -5895,5 +5895,94 @@ 30003=Le Champion est tombé ! 30004=Vous avez reçu un Parchemin de Puissance ! 30005=L'énergie du sanctuaire s'affaiblit en l'absence de challengers... +// [ 40000 - 40200 ] Vendeur du joueur +40000=%s n'a plus les moyens de s'entretenir et a été renvoyé. +40001=Impossible de créer l'acte de vendeur. Renvoi du vendeur annulé. +40002=Objets du vendeur retournés - banque : %i, terrain : %i. +40003=Objets du vendeur retournés - sac : %i, banque : %i, terrain : %i. +40004=%i pièces d'or retournées dans votre sac. +40005=%i pièces d'or retournées dans votre coffre. +40006=%i pièces d'or déposées à l'emplacement du vendeur. +40007=Acte de vendeur placé dans votre sac. +40008=Acte de vendeur placé dans votre coffre. +40009=Acte de vendeur déposé à l'emplacement du vendeur. 40010 = %s n'avait plus les moyens de payer l'entretien et a été renvoyé. +40011=%i objets déposés chez le vendeur. +40012=Ce vendeur n'a plus d'or à encaisser. +40013=Votre coffre bancaire ne peut pas recevoir les paiements des vendeurs pour le moment. +40014=Impossible de créer un chèque bancaire pour l'or du vendeur. +40015=%i pièces d'or encaissées sous forme de chèque bancaire dans votre coffre bancaire. +40016 = Impossible de déposer l'or du vendeur dans votre coffre bancaire. +40017=%i pièces d'or encaissées dans votre coffre bancaire. +40018=Montant du dépôt invalide. +40019=Vous n'avez pas de sac à dos. +40020=Vous n'avez pas assez d'or dans votre sac à dos. +40021=Le compte bancaire du vendeur est plein. 40022 = Impossible de retirer l'or de votre inventaire. Dépôt annulé. +40023=%i pièces d'or déposées sur le compte bancaire du vendeur. +40024=Solde maximum atteint sur le compte bancaire du vendeur. +40025=Compte bancaire du vendeur vide. +40026=%i pièces d'or transférées du compte bancaire du vendeur vers votre réserve. +40027=Impossible de créer l'acte de vente. Renvoi annulé. +40028=Je ne travaille pas pour vous ! 40029=Voir les articles +40030=Personnaliser +40031=Collecter de l'or +40032=Déposer de l'or +40033=Vider la banque +40034=Annuler +40035=Nombre de jours ouvrables : +40036=Heures ouvrables : +40037=Or détenu pour vous : +40038=Or détenu sur mon compte : +40039=Frais d'entretien du vendeur : +40040=Tarif par période : +40041=Heures de facturation : +40042=Total des frais par article : +40043=Illimité +40044=Désactivé +40045=PRINCIPAL +40046=PERSONNALISATION DU VENDEUR MENU +40047=CHEVEUX +40048=Renommer le vendeur +40049=Renommer la boutique +40050=Saisissez un nouveau nom de vendeur. +40051=Saisissez un nouveau nom de boutique. +40052=Saisissez le montant d'or que vous souhaitez déposer. +40053=Ce n'est pas un vendeur de joueur valide. +40054=Votre vendeur s'appelle désormais : %s +40055=Ce nom de boutique est trop long. Maximum 40 caractères. +40056=Le titre de votre boutique est désormais : %s +40057=Vous devez saisir un montant d'or valide. +40058=Barbe courte et moustache +40059=Vous ne pouvez pas faire d'achats dans cette demeure, car vous avez été banni de cet établissement. +40068=Barbe et Moustache +40069=Bouquet +40071=Enlever les cheveux +40072=Colorer les cheveux +40073=Barbe +40074=Enlever la barbe +40075=Colorer la barbe +40076=Changer pour un homme +40077=Changer pour une femme +40078=Changer pour un humain +40079=Changer pour un elfe +40080=Changer de vêtements +40081=FERMER +40082=COULEUR DES CHEVEUX +40083=COULEUR DE LA BARBE +40084=Cheveux - %s +40085=Barbe - %s +40086=MENU VÊTEMENTS DU VENDEUR +40087=Colorer +40088=Enlever +40089=CATÉGORIE DE COULEUR +40090=TERMINÉ +40091=VÊTEMENTS COULEUR +40100=Violet spécial +40101=Rouge spécial +40102=Orange spécial +40103=Jaune spécial +40104=Vert citron spécial +40105=Vert citron foncé spécial +40106=Bleu glacier spécial +40107=Blanc glacier spécial } EOF diff --git a/data/dictionaries/dictionary.GER b/data/dictionaries/dictionary.GER index 9014ffff0..64d2d1668 100644 --- a/data/dictionaries/dictionary.GER +++ b/data/dictionaries/dictionary.GER @@ -5731,5 +5731,97 @@ 30003=Der Champion ist besiegt! 30004=Du wurdest mit einer Schriftrolle der Macht belohnt! 30005=Die Energie des Schreins schwindet, da keine Herausforderer anwesend sind... +// [ 40000 - 40200 ] Spielerhändler +40000=%s kann sich den Unterhalt nicht mehr leisten und wurde entlassen. +40001=Händlerurkunde konnte nicht erstellt werden. Händlerentlassung abgebrochen. +40002=Händlergegenstände zurückgegeben – Bank: %i, Boden: %i. +40003=Händlergegenstände zurückgegeben – Rucksack: %i, Bank: %i, Boden: %i. +40004=%i Gold wurden deinem Rucksack hinzugefügt. +40005=%i Gold wurden deiner Bank hinzugefügt. +40006=%i Gold wurden am Händlerstandort abgelegt. +40007=Händlerurkunde wurde deinem Rucksack hinzugefügt. +40008=Händlerurkunde wurde deiner Bank hinzugefügt. +40009=Händlerurkunde wurde am Händlerstandort abgelegt. +40010=%s konnte sich den Unterhalt nicht leisten und wurde entlassen. +40011=%i Händlerartikel wurden am Händlerstandort abgelegt. +40012=Dieser Händler hat kein Gold zum Einsammeln. +40013=Ihr Bankfach kann derzeit keine Händlerauszahlungen empfangen. +40014=Es konnte kein Bankscheck für Händlergold erstellt werden. +40015=%i Gold wurden als Bankscheck in Ihrem Bankfach eingesammelt. +40016=Es konnte kein Händlergold in Ihr Bankfach eingezahlt werden. +40017=%i Gold wurden in Ihrem Bankfach eingesammelt. +40018=Ungültiger Einzahlungsbetrag. +40019=Sie haben keinen Rucksack. +40020=Sie haben nicht genügend Gold in Ihrem Rucksack. +40021=Das Händlerbankkonto ist voll. +40022=Gold konnte nicht aus Ihrem Paket entnommen werden. Einzahlung abgebrochen. +40023=%i Gold wurden auf das Händlerbankkonto eingezahlt. +40024=Das Händlerbankkonto hat seinen maximalen Kontostand erreicht. +40025=Das Händlerbankkonto ist leer. +40026=%i Gold wurden vom Händlerbankkonto auf das gehaltene Goldkonto übertragen. +40027=Händlervertrag konnte nicht erstellt werden. Kündigung abgebrochen. +40028=Ich arbeite nicht für Sie! +40029=Waren ansehen +40030=Anpassen +40031=Gold sammeln +40032=Gold einzahlen +40033=Bank leeren +40034=Schließen +40035=Anzahl der Tage, an denen ich arbeiten kann: +40036=Stunden, die ich arbeiten kann: +40037=Für Sie reserviertes Gold: +40038=Gold auf meinem Konto: +40039=Händlerunterhalt: +40040=Meine Gebühr pro Periode: +40041=Stunden der Abrechnungsperiode: +40042=Gesamtgebühr für Artikel: +40043=Unbegrenzt +40044=Deaktiviert +40045=HAUPT +40046=HÄNDLERANPASSUNG MENÜ +40047=HAAR +40048=Händler umbenennen +40049=Laden umbenennen +40050=Geben Sie einen neuen Händlernamen ein. +40051=Geben Sie einen neuen Ladennamen ein. +40052=Geben Sie die Menge an Gold ein, die Sie einzahlen möchten. +40053=Dies ist kein gültiger Spielerhändler. +40054=Ihr Händler heißt nun: %s +40055=Dieser Ladenname ist zu lang. Maximal 40 Zeichen. +40056=Ihr Ladentitel lautet nun: %s +40057=Sie müssen einen gültigen Goldbetrag eingeben. +40058=Kurzer Bart und Schnurrbart +40059=Sie können in diesem Heim keine Einkäufe tätigen, da Sie von dieser Einrichtung ausgeschlossen wurden. +40068=Langer Bart und Schnurrbart Schnurrbart +40069=Ziegenbart +40071=Haare entfernen +40072=Haare färben +40073=BART +40074=Bart entfernen +40075=Bart färben +40076=Zu Mann wechseln +40077=Zu Frau wechseln +40078=Zu Mensch wechseln +40079=Zu Elf wechseln +40080=Kleidung ändern +40081=SCHLIESSEN +40082=HAARFARBE +40083=BARTFARBE +40084=Haare - %s +40085=Bart - %s +40086=KLEIDUNGSMENÜ DES HÄNDLERS +40087=Farbe +40088=Entfernen +40089=FARBKATEGORIE +40090=FERTIG +40091=KLEIDUNG FARBE +40100=Speziallila +40101=Spezialrot +40102=Spezialorange +40103=Spezialgelb +40104=Speziallimette +40105=Spezialdunkellimette +40106=Spezialeisblau +40107=Spezialeisweiß } EOF diff --git a/data/dictionaries/dictionary.ITA b/data/dictionaries/dictionary.ITA index 15acb8adf..c6549b19a 100644 --- a/data/dictionaries/dictionary.ITA +++ b/data/dictionaries/dictionary.ITA @@ -5730,5 +5730,97 @@ 30003=Il Campione è stato sconfitto! 30004=Hai ricevuto in ricompensa una Pergamena del Potere! 30005=L'energia del santuario si affievolisce in assenza di sfidanti... +// [ 40000 - 40200 ] Venditore +40000=%s non può più permettersi il mantenimento ed è stato licenziato. +40001=Impossibile creare l'atto di vendita. Licenziamento del venditore annullato. +40002=Oggetti del venditore restituiti - banca: %i, terreno: %i. +40003=Oggetti del venditore restituiti - pacco: %i, banca: %i, terreno: %i. +40004=Restituito %i oro nel tuo pacco. +40005=Restituito %i oro nella tua banca. +40006=Lasciato %i oro nella posizione del venditore. +40007=Atto di vendita inserito nel tuo pacco. +40008=Atto di vendita inserito nella tua banca. +40009=Atto di vendita inserito nella posizione del venditore. +40010=%s non poteva permettersi il mantenimento ed è stato licenziato. +40011=Ha lasciato %i oggetti del venditore nella posizione del venditore. +40012=Questo venditore non ha oro da riscuotere. +40013=La tua cassetta della posta non può ricevere pagamenti dal venditore in questo momento. +40014=Impossibile creare un assegno bancario per l'oro del venditore. +40015=Hai raccolto %i oro come assegno bancario nella tua cassetta della posta. +40016=Impossibile depositare l'oro del venditore nella tua cassetta della posta. +40017=Hai raccolto %i oro nella tua cassetta della posta. +40018=Importo del deposito non valido. +40019=Non hai uno zaino. +40020=Non hai abbastanza oro nel tuo zaino. +40021=Il conto bancario del venditore è pieno. +40022=Impossibile rimuovere l'oro dal tuo pacchetto. Deposito annullato. +40023=Depositato %i oro sul conto bancario del venditore. +40024=Il conto bancario del venditore ha raggiunto il saldo massimo. +40025=Il conto bancario del venditore è vuoto. +40026=Spostato %i oro dal conto bancario del venditore all'oro trattenuto. +40027=Impossibile creare l'atto di vendita del venditore. Ignora annullato. +40028=Non lavoro per te! +40029=Vedi beni +40030=Personalizza +40031=Raccogli oro +40032=Deposita oro +40033=Svuota banca +40034=Ignora +40035=Numero di giorni in cui posso lavorare: +40036=Ore in cui posso lavorare: +40037=Oro trattenuto per te: +40038=Oro trattenuto sul mio conto: +40039=Mantenimento venditore: +40040=La mia tariffa per periodo è: +40041=Ore del periodo di addebito: +40042=Totale commissione articolo: +40043=Illimitato +40044=Disabilitato +40045=PRINCIPALE +40046=PERSONALIZZAZIONE VENDITORE MENU +40047=CAPELLI +40048=Rinomina venditore +40049=Rinomina negozio +40050=Inserisci un nuovo nome per il venditore. +40051=Inserisci un nuovo nome per il negozio. +40052=Inserisci la quantità d'oro che desideri depositare. +40053=Non è un venditore gestito da un giocatore valido. +40054=Il tuo venditore ora si chiama: %s +40055=Il nome del negozio è troppo lungo. Massimo 40 caratteri. +40056=Il titolo del tuo negozio ora è: %s +40057=Devi inserire una quantità d'oro valida. +40058=Barba e baffi corti +40059=Non puoi fare acquisti in questa abitazione, poiché sei stato bandito da questa struttura. +40068=Barba lunga e Baffi +40069=Pizzetto +40071=Rimuovi peli +40072=Colora capelli +40073=BARBA +40074=Rimuovi barba +40075=Colora barba +40076=Cambia in maschile +40077=Cambia in femminile +40078=Cambia in umano +40079=Cambia in elfo +40080=Cambia abbigliamento +40081=CHIUDI +40082=COLORE CAPELLI +40083=COLORE BARBA +40084=Capelli - %s +40085=Barba - %s +40086=MENU ABBIGLIAMENTO VENDITORE +40087=Colore +40088=Rimuovi +40089=CATEGORIA COLORE +40090=FATTO +40091=ABBIGLIAMENTO COLORE +40100=Viola speciale +40101=Rosso speciale +40102=Arancione speciale +40103=Giallo speciale +40104=Lime speciale +40105=Lime scuro speciale +40106=Blu ghiaccio speciale +40107=Bianco ghiaccio speciale } EOF diff --git a/data/dictionaries/dictionary.POL b/data/dictionaries/dictionary.POL index 74b21e9a8..fa83710be 100644 --- a/data/dictionaries/dictionary.POL +++ b/data/dictionaries/dictionary.POL @@ -5731,5 +5731,97 @@ 30003=Mistrz został pokonany! 30004=Otrzymałeś Zwoj mocy! 30005=Energia kapliczki słabnie z powodu braku pretendentów... +// [ 40000 - 40200 ] Gracz Sprzedawca +40000=%s nie jest już w stanie utrzymać się i został zwolniony. +40001=Nie można utworzyć aktu własności sprzedawcy. Zwolnienie sprzedawcy zostało przerwane. +40002=Zwrócono przedmioty sprzedawcy - bank: %i, ziemia: %i. +40003=Zwrócono przedmioty sprzedawcy - paczka: %i, bank: %i, ziemia: %i. +40004=Zwrócono %i złota do twojego pakietu. +40005=Zwrócono %i złota do twojego skarbca. +40006=Upuszczono %i złota u sprzedawcy. +40007=Umieszczono akt własności sprzedawcy w twoim pakiecie. +40008=Umieszczono akt własności sprzedawcy w twoim skarbcu. +40009=Upuszczono akt własności sprzedawcy u sprzedawcy. +40010=%s nie mógł sobie pozwolić na utrzymanie i został zwolniony. +40011=Upuścił %i przedmiotów u sprzedawcy. +40012=Ten sprzedawca nie ma złota do odebrania. +40013=Twoja skrytka bankowa nie może obecnie przyjmować wypłat od sprzedawcy. +40014=Nie można utworzyć czeku bankowego na złoto sprzedawcy. +40015=Zebrano %i złota jako czek bankowy w Twojej skrytce bankowej. +40016=Nie można wpłacić złota sprzedawcy do Twojej skrytki bankowej. +40017=Zebrano %i złota do Twojej skrytki bankowej. +40018=Nieprawidłowa kwota wpłaty. +40019=Nie masz plecaka. +40020=Nie masz wystarczającej ilości złota w plecaku. +40021=Konto bankowe sprzedawcy jest pełne. +40022=Nie można usunąć złota z paczki. Depozyt anulowany. +40023=Wpłacono %i złota na konto bankowe sprzedawcy. +40024=Saldo banku sprzedawcy osiągnęło maksymalne saldo. +40025=Konto bankowe sprzedawcy jest puste. +40026=Przeniesiono %i złota z konta bankowego sprzedawcy do posiadanego złota. +40027=Nie można utworzyć aktu własności sprzedawcy. Anulowano anulowanie. +40028=Nie pracuję dla ciebie! +40029=Zobacz towary +40030=Dostosuj +40031=Odbierz złoto +40032=Wpłać złoto +40033=Opróżnij bank +40034=Odrzuć +40035=Liczba dni, w których mogę pracować: +40036=Godziny, w których mogę pracować: +40037=Złoto przechowywane dla Ciebie: +40038=Złoto przechowywane na moim koncie: +40039=Utrzymanie przez sprzedawcę: +40040=Moja opłata za okres wynosi: +40041=Godziny okresu rozliczeniowego: +40042=Całkowita opłata za przedmiot: +40043=Nieograniczony +40044=Wyłączony +40045=GŁÓWNY +40046=DOSTOSOWANIE PRZEZ SPRZEDAWCĘ MENU +40047=WŁOSY +40048=Zmień nazwę handlarza +40049=Zmień nazwę sklepu +40050=Wpisz nową nazwę handlarza. +40051=Wpisz nową nazwę sklepu. +40052=Wpisz ilość złota, którą chcesz wpłacić. +40053=To nie jest poprawny handlarz gracza. +40054=Twój handlarz nazywa się teraz: %s +40055=Ta nazwa sklepu jest zbyt długa. Maksymalnie 40 znaków. +40056=Tytuł twojego sklepu brzmi teraz: %s +40057=Musisz wpisać poprawną ilość złota. +40058=Krótka broda i wąsy +40059=Nie możesz robić zakupów w tym domu, ponieważ otrzymałeś zakaz wstępu do tego lokalu. +40068=Długa broda i Wąsy +40069=Kozia bródka +40071=Usuń włosy +40072=Kolor włosów +40073=BRODA +40074=Usuń brodę +40075=Kolor brody +40076=Zmień na mężczyznę +40077=Zmień na kobietę +40078=Zmień na człowieka +40079=Zmień na elfa +40080=Zmień ubranie +40081=ZAMKNIJ +40082=KOLOR WŁOSÓW +40083=KOLOR BRODY +40084=Włosy - %s +40085=Broda - %s +40086=MENU ODZIEŻY U DOSTAWCY +40087=Kolor +40088=Usuń +40089=KOLOR KATEGORIA +40090=GOTOWE +40091=KOLOR ODZIEŻY +40100=Specjalny fioletowy +40101=Specjalny czerwony +40102=Specjalny pomarańczowy +40103=Specjalny żółty +40104=Specjalny limonkowy +40105=Specjalny ciemnolimonkowy +40106=Specjalny lodowoniebieski +40107=Specjalny lodowobiały } EOF diff --git a/data/dictionaries/dictionary.PTG b/data/dictionaries/dictionary.PTG index 8b9aabf8a..84ede1e46 100644 --- a/data/dictionaries/dictionary.PTG +++ b/data/dictionaries/dictionary.PTG @@ -5731,5 +5731,97 @@ 30003=O Campeão caiu! 30004=Foste recompensado com um Pergaminho de Poder! 30005=A energia do santuário diminui sem desafiantes presentes... +// [ 40000 - 40200 ] Vendedor do Jogador +40000=%s já não pode suportar a manutenção e foi dispensado. +40001=Não foi possível criar a escritura do vendedor. Dispensa do vendedor abortada. +40002=Artigos do vendedor devolvidos - banco: %i, chão: %i. +40003=Artigos do vendedor devolvidos - mochila: %i, banco: %i, chão: %i. +40004=Devolvido %i de ouro para a sua mochila. +40005=Devolvido %i de ouro para o seu cofre no banco. +40006=Depositado %i de ouro no local do vendedor. +40007=Escritura do vendedor colocada na sua mochila. +40008=Escritura do vendedor colocada no seu cofre no banco. +40009=Escritura do vendedor depositada no local do vendedor. +40010=%s não tinha condições para suportar os custos de manutenção e foi despedido. +40011=Deixou %i artigo(s) do vendedor no local do vendedor. +40012=Este vendedor não tem ouro para recolher. +40013=O seu ATM não pode receber pagamentos de vendedores no momento. +40014=Não foi possível criar um cheque bancário para o ouro do vendedor. +40015=Coletou %i de ouro como um cheque bancário no seu ATM. +40016=Não foi possível depositar o ouro do vendedor no seu ATM. +40017=Coletou %i de ouro no seu ATM. +40018=Valor de depósito inválido. +40019=Não tem mochila. +40020=Não tem ouro suficiente na sua mochila. +40021=A conta bancária do vendedor está cheia. +40022=Não foi possível remover o ouro do seu pacote. Depósito cancelado. +40023=Depositado %i de ouro na conta bancária do fornecedor. +40024=O saldo bancário do fornecedor atingiu o máximo permitido. +40025=A conta bancária do fornecedor está vazia. +40026=Movido %i de ouro da conta bancária do fornecedor para o ouro retido. +40027=Não foi possível criar a escritura do fornecedor. Dispensa cancelada. +40028=Eu não trabalho para ti! +40029=Ver mercadorias +40030=Personalizar +40031=Coletar Ouro +40032=Depositar Ouro +40033=Levantar dinheiro +40034=Dispensar +40035=Nº de dias que posso trabalhar: +40036=Horas em que posso trabalhar: +40037=Ouro reservado para si: +40038=Ouro mantido na minha conta: +40039=Manutenção do fornecedor: +40040=A minha faturação por período é: +40041=Horas do período de cobrança: +40042=Total da taxa do artigo: +40043=Ilimitado +40044=Desativado +40045=PRINCIPAL +40046=PERSONALIZAÇÃO DO FORNECEDOR MENU +40047=CABELO +40048=Renomear Vendedor +40049=Renomear Loja +40050=Introduza um novo nome para o vendedor. +40051=Introduza um novo nome para a loja. +40052=Introduza a quantidade de ouro que pretende depositar. +40053=Este não é um vendedor de jogadores válido. +40054=O seu vendedor chama-se agora: %s +40055=O nome da loja é demasiado longo. Máximo de 40 caracteres. +40056=O nome da sua loja é agora: %s +40057=Tem de introduzir uma quantidade de ouro válida. +40058=Barba e Bigode Curtos +40059=Não pode comprar nesta casa, pois foi banido deste estabelecimento. +40068=Barba comprida e Bigode +40069=Cavahaque +40071=Remover Cabelo +40072=Colorir Cabelo +40073=Barba +40074=Remover Barba +40075=Colorir Barba +40076=Mudar para Masculino +40077=Mudar para Feminino +40078=Mudar para Humano +40079=Mudar para Elfo +40080=Mudar Roupa +40081=FECHAR +40082=COR DO CABELO +40083=COR DA BARBA +40084=Cabelo - %s +40085=Barba - %s +40086=MENU DE VESTUÁRIO DO FORNECEDOR +40087=Colorir +40088=Remover +40089=COR CATEGORIA +40090=CONCLUÍDO +40091=COR DA ROUPA +40100=Roxo Especial +40101=Vermelho Especial +40102=Laranja Especial +40103=Amarelo Especial +40104=Verde Limão Especial +40105=Verde Limão Escuro Especial +40106=Azul Gelo Especial +40107=Branco Gelo Especial } EOF diff --git a/data/dictionaries/dictionary.SPA b/data/dictionaries/dictionary.SPA index 3d415c99b..5aee93790 100644 --- a/data/dictionaries/dictionary.SPA +++ b/data/dictionaries/dictionary.SPA @@ -5730,5 +5730,95 @@ 30003=¡El Campeón ha caído! 30004=¡Has sido recompensado con un Pergamino de Poder! 30005=La energía del santuario disminuye al no haber desafiantes presentes... +// [ 40000 - 40200 ] Vendedor Jugador +40000=%s ya no puede costear el mantenimiento y ha sido despedido. +40001=No se pudo crear la escritura de vendedor. Se canceló la destitución del vendedor. +40002=Objetos de vendedor devueltos - banco: %i, suelo: %i. +40003=Objetos de vendedor devueltos - paquete: %i, banco: %i, suelo: %i. +40004=Se devolvieron %i de oro a tu paquete. +40005=Se devolvieron %i de oro a tu caja del banco. +40006=Se dejó %i de oro en la ubicación del vendedor. +40007=Se colocó la escritura de vendedor en tu paquete. +40008=Se colocó la escritura de vendedor en tu caja del banco. +40009=Se dejó la escritura de vendedor en la ubicación del vendedor. +40010=%s no pudo costear el mantenimiento y fue despedido. +40011=Se dejaron %i objetos de vendedor en la ubicación del vendedor. +40012=Este vendedor no tiene oro disponible para recolectar. +40013=Tu caja bancaria no puede recibir pagos de vendedor en este momento. +40014=No se puede crear un cheque bancario para el oro del vendedor. +40015=Se recaudó %i de oro como cheque bancario en tu caja bancaria. +40016=No se puede depositar oro de vendedor en tu caja bancaria. +40017=Se recaudó %i de oro en tu caja bancaria. +40018=Importe de depósito no válido. +40019=No tienes mochila. +40020=No tienes suficiente oro en tu mochila. +40021=La cuenta bancaria del vendedor está llena. 40022=No se pudo retirar oro de tu paquete. Depósito cancelado. +40023=Se depositó %i de oro en la cuenta bancaria del vendedor. +40024=El banco del vendedor alcanzó su saldo máximo. +40025=La cuenta bancaria del vendedor está vacía. +40026=Se transfirió %i de oro de la cuenta bancaria del vendedor al oro retenido. +40027=No se pudo crear la escritura del vendedor. Descartar cancelado. +40028=¡No trabajo para ti! 40029=Ver artículos +40030=Personalizar +40031=Recoger oro +40032=Depositar oro +40033=Fluir banco +40034=Descartar +40035=Días que puedo trabajar: +40036=Horas que puedo trabajar: +40037=Oro guardado para ti: +40038=Oro guardado en mi cuenta: +40039=Mantenimiento del vendedor: +40040=Mi cargo por período es: +40041=Horas del período de cobro: +40042=Total del cargo por artículo: +40043=Ilimitado +40044=Desactivado +40045=PRINCIPAL +40046=PERSONALIZACIÓN DEL VENDEDOR MENÚ +40047=CABELLO +40048=Cambiar nombre de vendedor +40049=Cambiar nombre de tienda +40050=Introduce un nuevo nombre de vendedor. +40051=Introduce un nuevo nombre de tienda. +40052=Introduce la cantidad de oro que deseas depositar. +40053=Ese vendedor no es válido para el jugador. +40054=Tu vendedor ahora se llama: %s +40055=El nombre de la tienda es demasiado largo. Máximo 40 caracteres. +40056=El nombre de tu tienda ahora es: %s +40057=Debes introducir una cantidad válida de oro. +40058=Barba y bigote cortos +40059=No puedes comprar en esta casa porque has sido expulsado del establecimiento. +40068=Barba larga y bigote Bigote +40069=Perilla +40071=Quitar pelo +40072=Color de pelo +40073=BARBA +40074=Quitar barba +40075=Color de barba +40076=Cambiar a masculino +40077=Cambiar a femenino +40078=Cambiar a humano +40079=Cambiar a elfo +40080=Cambiar ropa +40081=CERRAR +40082=COLOR DE PELO +40083=COLOR DE BARBA +40084=Cabello - %s +40085=Barba - %s +40086=MENÚ DE ROPA DEL VENDEDOR +40087=Color +40088=Quitar +40089=COLOR CATEGORÍA +40090=HECHO +40091=COLOR DE LA ROPA +40100=Morado especial +40101=Rojo especial +40102=Naranja especial +40103=Amarillo especial +40104=Lima especial +40105=Lima oscuro especial +40106=Azul hielo especial +40107=Blanco hielo especial } EOF diff --git a/data/dictionaries/dictionary.ZRO b/data/dictionaries/dictionary.ZRO index eadedcad9..755341cbd 100644 --- a/data/dictionaries/dictionary.ZRO +++ b/data/dictionaries/dictionary.ZRO @@ -5735,5 +5735,97 @@ 30003=The Champion has fallen! 30004=You have been rewarded with a Scroll of Power! 30005=The shrine's energy wanes with no challengers present... +// [ 40000 - 40200 ] Player Vendor +40000=%s can no longer afford upkeep and has been dismissed. +40001=Unable to create vendor deed. Vendor dismissal aborted. +40002=Vendor items returned - bank: %i, ground: %i. +40003=Vendor items returned - pack: %i, bank: %i, ground: %i. +40004=Returned %i gold to your pack. +40005=Returned %i gold to your bank box. +40006=Dropped %i gold at vendor location. +40007=Placed vendor deed in your pack. +40008=Placed vendor deed in your bank box. +40009=Dropped vendor deed at vendor location. +40010=%s could not afford upkeep and was dismissed. +40011=Dropped %i vendor item(s) at vendor location. +40012=This vendor has no held gold to collect. +40013=Your bank box cannot receive vendor payouts right now. +40014=Unable to create a bank check for vendor gold. +40015=Collected %i gold as a bank check in your bank box. +40016=Unable to deposit vendor gold into your bank box. +40017=Collected %i gold to your bank box. +40018=Invalid deposit amount. +40019=You have no backpack. +40020=You do not have enough gold in your pack. +40021=Vendor bank account is full. +40022=Unable to remove gold from your pack. Deposit cancelled. +40023=Deposited %i gold into vendor bank account. +40024=Vendor bank reached its maximum balance. +40025=Vendor bank account is empty. +40026=Moved %i gold from vendor bank account to held gold. +40027=Unable to create vendor deed. Dismiss cancelled. +40028=I don't work for you! +40029=See goods +40030=Customize +40031=Collect Gold +40032=Deposit Gold +40033=Flush Bank +40034=Dismiss +40035=Amount of days I can work: +40036=Hours I can work: +40037=Gold held for you: +40038=Gold held in my account: +40039=Vendor upkeep: +40040=My charge per period is: +40041=Charge period hours: +40042=Item fee total: +40043=Unlimited +40044=Disabled +40045=MAIN +40046=VENDOR CUSTOMIZATION MENU +40047=HAIR +40048=Rename Vendor +40049=Rename Shop +40050=Enter a new vendor name. +40051=Enter a new shop name. +40052=Enter the amount of gold you wish to deposit. +40053=That is not a valid player vendor. +40054=Your vendor is now named: %s +40055=That shop name is too long. Maximum 40 chars. +40056=Your shop title is now: %s +40057=You must enter a valid gold amount. +40058=Short Beard and Mustache +40059=You can't shop from this home as you have been banned from this establishment. +40068=Long Beard and Mustache +40069=Goatee +40071=Remove Hair +40072=Color Hair +40073=BEARD +40074=Remove Beard +40075=Color Beard +40076=Change to Male +40077=Change to Female +40078=Change to Human +40079=Change to Elf +40080=Change Clothing +40081=CLOSE +40082=HAIR COLOR +40083=BEARD COLOR +40084=Hair - %s +40085=Beard - %s +40086=VENDOR CLOTHING MENU +40087=Color +40088=Remove +40089=COLOR CATEGORY +40090=DONE +40091=CLOTHING COLOR +40100=Special Purple +40101=Special Red +40102=Special Orange +40103=Special Yellow +40104=Special Lime +40105=Special Dark Lime +40106=Special Ice Blue +40107=Special Ice White } EOF diff --git a/data/js/coreDefinitions.json b/data/js/coreDefinitions.json index cb3b43f61..36de64c6b 100644 --- a/data/js/coreDefinitions.json +++ b/data/js/coreDefinitions.json @@ -4266,6 +4266,12 @@ "cppType": "JSPROP_ENUMANDPERM", "type": "number" }, + { + "name": "vendorGoldHeld", + "cppName": "CCP_VENDORGOLDHELD", + "cppType": "JSPROP_ENUMANDPERM", + "type": "number" + }, { "name": "willhunger", "cppName": "CCP_HUNGERSTATUS", diff --git a/data/js/definitions.d.ts b/data/js/definitions.d.ts index 6aa24e82d..e69de29bb 100644 Binary files a/data/js/definitions.d.ts and b/data/js/definitions.d.ts differ diff --git a/data/js/jse_fileassociations.scp b/data/js/jse_fileassociations.scp index 8fb66b609..33675fa44 100644 --- a/data/js/jse_fileassociations.scp +++ b/data/js/jse_fileassociations.scp @@ -148,6 +148,7 @@ 3107=npc/pets/petbonding.js 3108=server/network/0xbf_sendnpcghostmode.js 3109=npc/ai/veterinarian.js +3110=npc/ai/playervendor.js // ------------------------ // AI-Stuff diff --git a/data/js/npc/ai/playervendor.js b/data/js/npc/ai/playervendor.js new file mode 100644 index 000000000..88fc8da89 --- /dev/null +++ b/data/js/npc/ai/playervendor.js @@ -0,0 +1,2063 @@ +/// +// @ts-check +// Version: 1.1.7 + +const VendorSettings = { + // Max amount of money the vendor can hold in the bank. + // Held gold is not included in this limit, but will + // be used first to pay upkeep before bank funds are used. + get MaxFunds() { return GetServerSetting( "VendorMaxFunds" ); }, + // If true, returned vendor belongings skip player backpack entirely. + // Items, gold and vendor deed will try bank box first, then drop at vendor location. + // If false, belongings try backpack first, then bank box, then ground. + get OnlyReturnToBank() { return GetServerSetting( "onlyReturnToBank" ); }, + get ChargesEnabled() { return GetServerSetting( "VendorChargesEnabled" ); },// Master toggle for vendor upkeep charges + get BaseCharge() { return GetServerSetting( "VendorBaseCharge" ); },// Flat fee per charge period + get ChargeHours() { return GetServerSetting( "VendorChargeHours" ); },// Charge every X real hours + get ItemFeesEnabled() { return GetServerSetting( "VendorUseItemFeesEnabled" ); },// Add item-based fee from listed item prices + get ItemFeeDivisor() { return GetServerSetting( "VendorItemFeeDivisor" ); },// 3 gold per 500 worth of one item + get ItemFeeAmount() { return GetServerSetting( "VendorItemFeeAmount" ); } // Fee added per divisor step +} + +const VendorTimer = { + Upkeep: 90 +}; + +const VendorEquipmentLayer = { + OneHand: 0x01, + TwoHand: 0x02, + Shoes: 0x03, + Pants: 0x04, + Shirt: 0x05, + Helm: 0x06, + Gloves: 0x07, + Ring: 0x08, + Neck: 0x0A, + Hair: 0x0B, + Waist: 0x0C, + InnerTorso: 0x0D, + Bracelet: 0x0E, + Beard: 0x10, + MiddleTorso: 0x11, + Earrings: 0x12, + Arms: 0x13, + Cloak: 0x14, + OuterTorso: 0x16, + OuterLegs: 0x17 +}; + +const vendorHairHueCategories = [ + { name: null, hues: [ 0x044E, 0x044F, 0x0450, 0x0451, 0x0452, 0x0453, 0x0454 ], dictID: 17111 }, // Black + { name: null, hues: [ 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C ], dictID: 17112 }, // Beige + { name: null, hues: [ 0x045D, 0x045E, 0x045F, 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, 0x0468, 0x0469, 0x046A, 0x046B, 0x046C ], dictID: 17113 }, // Golden + { name: null, hues: [ 0x046D, 0x046E, 0x046F, 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0476, 0x0477, 0x0478, 0x0479, 0x047A, 0x047B, 0x047C ], dictID: 17114 }, // Dark Brown + { name: null, hues: [ 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7, 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF, 0x04C0 ], dictID: 17108 }, // Red + { name: null, hues: [ 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC ], dictID: 17105 }, // Auburn + { name: null, hues: [ 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, 0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F, 0x0660 ], dictID: 17103 }, // Brown + { name: null, hues: [ 0x08A5, 0x08A6, 0x08A7, 0x08A8, 0x08A9, 0x0961, 0x0962, 0x0963, 0x0964, 0x0965, 0x0966, 0x0967, 0x0968 ], dictID: 17110 }, // Blonde + { name: null, hues: [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ], dictID: 40100 }, // Special Purple + { name: null, hues: [ 32, 33, 34, 35, 36, 37 ], dictID: 40101 }, // Special Red + { name: null, hues: [ 38, 39, 40, 41, 42, 43, 44, 45, 46 ], dictID: 40102 }, // Special Orange + { name: null, hues: [ 54, 55, 56, 57 ], dictID: 40103 }, // Special Yellow + { name: null, hues: [ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72 ], dictID: 40104 }, // Special Lime + { name: null, hues: [ 81, 82, 83 ], dictID: 40105 }, // Special Dark Lime + { name: null, hues: [ 89, 90, 91 ], dictID: 40106 }, // Special Ice Blue + { name: null, hues: [ 1153, 1154, 1155 ], dictID: 40107 } // Special Ice White +]; + +const femaleElfHairStyles = [ + { itemID: 0x2FCC, name: null, dictID: 18035 }, // Flower + { itemID: 0x2FC0, name: null, dictID: 18028 }, // Long Feather + { itemID: 0x2FC1, name: null, dictID: 18029 }, // Short + { itemID: 0x2FC2, name: null, dictID: 18030 }, // Mullet + { itemID: 0x2FCE, name: null, dictID: 18032 }, // Topknot + { itemID: 0x2FCF, name: null, dictID: 18033 }, // Long Braid + { itemID: 0x2FD0, name: null, dictID: 18036 }, // Buns + { itemID: 0x2FD1, name: null, dictID: 18034 } // Spiked +]; + +const maleElfHairStyles = [ + { itemID: 0x2FBF, name: null, dictID: 18027 }, // Mid Long + { itemID: 0x2FC0, name: null, dictID: 18028 }, // Long Feather + { itemID: 0x2FC1, name: null, dictID: 18029 }, // Short + { itemID: 0x2FC2, name: null, dictID: 18030 }, // Mullet + { itemID: 0x2FCE, name: null, dictID: 18032 }, // Topknot + { itemID: 0x2FCF, name: null, dictID: 18033 }, // Long Braid + { itemID: 0x2FCD, name: null, dictID: 18031 }, // Long + { itemID: 0x2FD1, name: null, dictID: 18034 } // Spiked +]; + +const humanHairStyles = [ + { itemID: 0x203B, name: null, dictID: 18029 }, // Short + { itemID: 0x203C, name: null, dictID: 18031 }, // Long + { itemID: 0x203D, name: null, dictID: 2086 }, // Ponytail + { itemID: 0x2044, name: null, dictID: 2087 }, // Mohawk + { itemID: 0x2045, name: null, dictID: 2080 }, // Pageboy + { itemID: 0x204A, name: null, dictID: 18032 }, // Topknot + { itemID: 0x2047, name: null, dictID: 2088 }, // Curly + { itemID: 0x2048, name: null, dictID: 2081 }, // Receding + { itemID: 0x2049, name: null, dictID: 2082 } // 2-Tails +]; + +const humanBeardStyles = [ + { itemID: 0x2041, name: null, dictID: 18001 }, // Mustache + { itemID: 0x203F, name: null, dictID: 18002 }, // Short Beard + { itemID: 0x204B, name: null, dictID: 40058 }, // Short Beard and Mustache + { itemID: 0x203E, name: null, dictID: 18005 }, // Long Beard + { itemID: 0x204C, name: null, dictID: 40068 }, // Long Beard and Mustache + { itemID: 0x2040, name: null, dictID: 40069 }, // Goatee + { itemID: 0x204D, name: null, dictID: 18000 } // Vandyke +]; + +const vendorClothingCategories = [ + { layer: VendorEquipmentLayer.InnerTorso, name: "Upper Torso", canDye: true, compactLayout: false, entries: [ + { itemID: 0x1517, name: "Shirt" }, + { itemID: 0x1EFD, name: "Fancy Shirt" }, + { itemID: 0x1F01, name: "Plain Dress" }, + { itemID: 0x1EFF, name: "Fancy Dress" }, + { itemID: 0x1F03, name: "Robe" } + ]}, + + { layer: VendorEquipmentLayer.MiddleTorso, name: "Over Chest", canDye: true, compactLayout: false, entries: [ + { itemID: 0x1F7B, name: "Doublet" }, + { itemID: 0x1FA1, name: "Tunic" }, + { itemID: 0x1F9F, name: "Jester Suit" }, + { itemID: 0x1541, name: "Body Sash" }, + { itemID: 0x1FFD, name: "Surcoat" }, + { itemID: 0x153B, name: "Half Apron" }, + { itemID: 0x153D, name: "Full Apron" } + ]}, + + { layer: VendorEquipmentLayer.Shoes, name: "Footwear", canDye: true, compactLayout: false, entries: [ + { itemID: 0x170D, name: "Sandals" }, + { itemID: 0x1710, name: "Shoes" }, + { itemID: 0x170B, name: "Boots" }, + { itemID: 0x1711, name: "Thigh Boots" } + ]}, + + { layer: VendorEquipmentLayer.Helm, name: "Hats", canDye: true, compactLayout: false, entries: [ + { itemID: 0x1544, name: "Skull Cap" }, + { itemID: 0x1540, name: "Bandana" }, + { itemID: 0x1713, name: "Floppy Hat" }, + { itemID: 0x1714, name: "Wide Brim Hat" }, + { itemID: 0x1715, name: "Cap" }, + { itemID: 0x1716, name: "Tall Straw Hat" }, + { itemID: 0x1717, name: "Straw Hat" }, + { itemID: 0x1718, name: "Wizard Hat" }, + { itemID: 0x1719, name: "Bonnet" }, + { itemID: 0x171A, name: "Feathered Hat" }, + { itemID: 0x171B, name: "Tricorne Hat" }, + { itemID: 0x171C, name: "Jester Hat" } + ]}, + + { layer: VendorEquipmentLayer.Pants, name: "Lower Torso", canDye: true, compactLayout: false, entries: [ + { itemID: 0x1539, name: "Long Pants" }, + { itemID: 0x1537, name: "Kilt" }, + { itemID: 0x1516, name: "Skirt" } + ]}, + + { layer: VendorEquipmentLayer.Cloak, name: "Back", canDye: true, compactLayout: false, entries: [ + { itemID: 0x1515, name: "Cloak" } + ]}, + + { layer: VendorEquipmentLayer.OneHand, name: "Held Items", canDye: false, compactLayout: true, entries: [ + { itemID: 0x0DBF, name: "Fishing Pole" }, + { itemID: 0x0E86, name: "Pickaxe" }, + { itemID: 0x0E87, name: "Pitchfork" }, + { itemID: 0x0EC3, name: "Cleaver" }, + { itemID: 0x0F5C, name: "Mace" }, + { itemID: 0x0F6B, name: "Torch" }, + { itemID: 0x102A, name: "Hammer" }, + { itemID: 0x0F61, name: "Longsword" }, + { itemID: 0x13F8, name: "Gnarled Staff" }, + { itemID: 0x0F4F, name: "Crossbow" }, + { itemID: 0x1407, name: "War Mace" }, + { itemID: 0x1443, name: "Two-Handed Axe" }, + { itemID: 0x0F62, name: "Spear" }, + { itemID: 0x13FF, name: "Katana" }, + { itemID: 0x0EFA, name: "Spellbook" } + ]} +]; + +/** @param {VendorHueCategory} entry @param {Socket|null} socket @returns {string} */ +function GetVendorHueCategoryName( entry, socket ) +{ + if( entry.dictID && socket ) + return GetDictionaryEntry( entry.dictID, socket.language ); + + return entry.name || ""; +} + +/** @param {HairOrBeard} entry @param {Socket} socket @returns {string} */ +function GetHairOrBeardName( entry, socket ) +{ + if( entry.dictID && socket ) + return GetDictionaryEntry( entry.dictID, socket.language ); + + return entry.name || ""; +} + +/** @param {Item} itemObj @returns {number} */ +function GetVendorItemPeriodFee( itemObj ) +{ + if( itemObj.vendorPrice <= 0 || !VendorSettings.ItemFeesEnabled || VendorSettings.ItemFeeDivisor <= 0 || VendorSettings.ItemFeeAmount <= 0 ) + return 0; + + return Math.floor( itemObj.vendorPrice / VendorSettings.ItemFeeDivisor ) * VendorSettings.ItemFeeAmount; +} + +/** @type { ( vendor: Character ) => number } */ +function GetVendorItemFeeTotal( vendor ) +{ + if( vendor.aitype != 17 || !ValidateObject( vendor.pack ) ) + return 0; + + var totalFee = 0; + var packItem = vendor.pack.FirstItem(); + for( ; !vendor.pack.FinishedItems(); packItem = vendor.pack.NextItem() ) + { + if( ValidateObject( packItem )) + totalFee += GetVendorItemPeriodFee( packItem ); + } + + return totalFee; +} + +/** @type { ( vendor: Character ) => number } */ +function GetVendorChargePerPeriod( vendor ) +{ + if( vendor.aitype != 17 || !VendorSettings.ChargesEnabled ) + return 0; + + var overrideValue = vendor.GetTag( "VendorChargePerPeriod" ); + if( overrideValue > 0 ) + return overrideValue; + + return VendorSettings.BaseCharge + ( VendorSettings.ItemFeesEnabled ? GetVendorItemFeeTotal( vendor ) : 0 ); +} + +/** @param {Character} vendor @param {number} amount @returns {number} */ +function DepositVendorBankGold( vendor, amount ) +{ + if( vendor.aitype != 17 || amount <= 0 ) + return 0; + + var bankGold = vendor.GetTag( "VendorBankAccount" ); + if( bankGold < 0 ) + bankGold = 0; + + var roomLeft = VendorSettings.MaxFunds - bankGold; + if( roomLeft <= 0 ) + return 0; + + if( amount > roomLeft ) + amount = roomLeft; + + vendor.SetTag( "VendorBankAccount", bankGold + amount ); + return amount; +} + +/** @param {Character} vendor @param {number} amount @returns {number} */ +function WithdrawVendorBankGold( vendor, amount ) +{ + if( vendor.aitype != 17 || amount <= 0 ) + return 0; + + var bankGold = vendor.GetTag( "VendorBankAccount" ); + if( bankGold < 0 ) + bankGold = 0; + + if( amount > bankGold ) + amount = bankGold; + + vendor.SetTag( "VendorBankAccount", bankGold - amount ); + return amount; +} + +/** @param {Character} vendor @param {number} charge @returns {boolean} */ +function PayVendorCharge( vendor, charge ) +{ + if( vendor.aitype != 17 || charge <= 0 ) + return false; + + var bankGold = vendor.GetTag( "VendorBankAccount" ); + var heldGold = vendor.vendorGoldHeld || 0; + + if( bankGold < 0 ) + bankGold = 0; + + if(( bankGold + heldGold ) < charge ) + return false; + + if( bankGold >= charge ) + { + vendor.SetTag( "VendorBankAccount", bankGold - charge ); + return true; + } + + charge -= bankGold; + vendor.SetTag( "VendorBankAccount", 0 ); + vendor.vendorGoldHeld = heldGold - charge; + return true; +} + +/** @param {Character} vendor @returns {number} */ +function FlushVendorBankToHeldGold( vendor ) +{ + if( vendor.aitype != 17 ) + return 0; + + var bankGold = vendor.GetTag( "VendorBankAccount" ); + if( bankGold <= 0 ) + return 0; + + vendor.SetTag( "VendorBankAccount", 0 ); + vendor.vendorGoldHeld = ( vendor.vendorGoldHeld || 0 ) + bankGold; + return bankGold; +} + +/** @param {Character} vendor @returns {number} */ +function GetVendorPeriodsAffordable( vendor ) +{ + var perPeriod = GetVendorChargePerPeriod( vendor ); + if( perPeriod <= 0 ) + return 0; + + var bankGold = 0; + var heldGold = 0; + + if( vendor.aitype == 17 ) + { + bankGold = vendor.GetTag( "VendorBankAccount" ); + heldGold = vendor.vendorGoldHeld || 0; + + if( bankGold < 0 ) + bankGold = 0; + } + + return Math.floor(( bankGold + heldGold ) / perPeriod ); +} + +/** @param {Character} vendor @returns {number} */ +function GetVendorHoursAffordable( vendor ) +{ + return GetVendorPeriodsAffordable( vendor ) * VendorSettings.ChargeHours; +} + +/** @param {Character} vendor @returns {number} */ +function GetVendorDaysAffordable( vendor ) +{ + return Math.floor( GetVendorHoursAffordable( vendor ) / 24 ); +} + +/** @param {Item} itemObj @param {Character} vendor */ +function DropItemAtVendorLocation( itemObj, vendor ) +{ + itemObj.container = null; + itemObj.x = vendor.x; + itemObj.y = vendor.y; + itemObj.z = vendor.z; + itemObj.worldnumber = vendor.worldnumber; + itemObj.instanceID = vendor.instanceID; +} + +/** @param {Item} itemObj @param {Item} container @returns {boolean} */ +function TryMoveItemToContainer( itemObj, container ) +{ + if( !ValidateObject( container ) ) + return false; + + if( container.totalItemCount >= container.maxItems ) + return false; + + if(( container.weight + ( itemObj.weight * itemObj.amount ) ) > container.weightMax ) + return false; + + itemObj.container = container; + return ValidateObject( itemObj.container ) && itemObj.container.serial == container.serial; +} + +/** @param {Item} itemObj @param {Character} pUser @param {Character} vendor @returns {number} */ +function PlaceItemForOwnerOrDrop( itemObj, pUser, vendor ) +{ + var pack = pUser.pack; + var bankBox = pUser.FindItemLayer( 29 ); + + if( VendorSettings.OnlyReturnToBank ) + { + if( TryMoveItemToContainer( itemObj, bankBox ) ) + return 2; + + if( TryMoveItemToContainer( itemObj, pack ) ) + return 1; + } + else + { + if( TryMoveItemToContainer( itemObj, pack ) ) + return 1; + + if( TryMoveItemToContainer( itemObj, bankBox ) ) + return 2; + } + + DropItemAtVendorLocation( itemObj, vendor ); + return 3; +} + +/** @param {Character} vendor @param {Character} pUser @returns {{pack:number, bank:number, ground:number}} */ +function ReturnVendorItemsToOwner( vendor, pUser ) +{ + var results = { pack:0, bank:0, ground:0 }; + + if( vendor.aitype != 17 || !ValidateObject( vendor.pack )) + return results; + + var itemList = []; + var packItem = vendor.pack.FirstItem(); + for( ; !vendor.pack.FinishedItems(); packItem = vendor.pack.NextItem() ) + { + if( ValidateObject( packItem ) ) + itemList.push( packItem ); + } + + for( var i = 0; i < itemList.length; ++i ) + { + var placeResult = PlaceItemForOwnerOrDrop( itemList[i], pUser, vendor ); + if( placeResult == 1 ) + results.pack++; + else if( placeResult == 2 ) + results.bank++; + else if( placeResult == 3 ) + results.ground++; + } + + return results; +} + +/** @param {Character} vendor @returns {number} */ +function DropVendorItemsAtLocation( vendor ) +{ + if( vendor.aitype != 17 || !ValidateObject( vendor.pack ) ) + return 0; + + var itemList = []; + var packItem = vendor.pack.FirstItem(); + for( ; !vendor.pack.FinishedItems(); packItem = vendor.pack.NextItem() ) + { + if( ValidateObject( packItem )) + itemList.push( packItem ); + } + + for( var i = 0; i < itemList.length; ++i ) + { + DropItemAtVendorLocation( itemList[i], vendor ); + } + + return itemList.length; +} + +/** @param {Character} vendor @returns {Item|null} */ +function CreateVendorDismissDeedAtLocation( vendor ) +{ + if( vendor.aitype != 17 ) + return null; + + var deed = CreateBlankItem( null, vendor, 1, "A vendor deed for " + vendor.name + ".", 0x14F0, 0x0, "ITEM", false ); + if( !ValidateObject( deed ) ) + return null; + + deed.type = 217; + deed.buyvalue = 2000; + deed.SetTag( "vendorName", vendor.name ); + DropItemAtVendorLocation( deed, vendor ); + return deed; +} + +/** @param {Character} pUser @param {Character} vendor @returns {Item|null} */ +function CreateVendorDismissDeedForOwner( pUser, vendor ) +{ + if( vendor.aitype != 17 ) + return null; + + var deedName = "A vendor deed for " + vendor.name + "."; + if( pUser.socket ) + { + deedName = GetDictionaryEntry( 580, pUser.socket.language ); + deedName = deedName.replace( /%s/gi, vendor.name ); + } + + var deed = CreateBlankItem( null, pUser, 1, deedName, 0x14F0, 0x0, "ITEM", false ); + if( !ValidateObject( deed ) ) + return null; + + deed.type = 217; + deed.buyvalue = 2000; + deed.SetTag( "vendorName", vendor.name ); + return deed; +} + +/** @param {Character} vendor @returns {number} */ +function DropVendorGoldAtLocation( vendor ) +{ + if( vendor.aitype != 17 ) + return 0; + + var heldGold = vendor.vendorGoldHeld || 0; + var bankGold = vendor.GetTag( "VendorBankAccount" ); + if( bankGold < 0 ) + bankGold = 0; + + var totalGold = heldGold + bankGold; + if( totalGold <= 0 ) + return 0; + + var goldItem = CreateDFNItem( null, vendor, "0x0EED", totalGold, "ITEM", false ); + if( !ValidateObject( goldItem ) ) + return 0; + + vendor.vendorGoldHeld = 0; + vendor.SetTag( "VendorBankAccount", 0 ); + DropItemAtVendorLocation( goldItem, vendor ); + return totalGold; +} + +/** @param {Character} pUser @param {Character} vendor @returns {{amount:number, location:number}} */ +function ReturnVendorGoldToOwner( pUser, vendor ) +{ + var result = { amount:0, location:0 }; + + if( vendor.aitype != 17 ) + return result; + + var heldGold = vendor.vendorGoldHeld || 0; + var bankGold = vendor.GetTag( "VendorBankAccount" ); + if( bankGold < 0 ) + bankGold = 0; + + var totalGold = heldGold + bankGold; + if( totalGold <= 0 ) + return result; + + var goldItem = CreateDFNItem( null, pUser, "0x0EED", totalGold, "ITEM", false ); + if( !ValidateObject( goldItem ) ) + return result; + + var placeResult = PlaceItemForOwnerOrDrop( goldItem, pUser, vendor ); + if( placeResult == 0 ) + return result; + + vendor.vendorGoldHeld = 0; + vendor.SetTag( "VendorBankAccount", 0 ); + + result.amount = totalGold; + result.location = placeResult; + return result; +} + +/** @param {Character} vendor */ +function FinalizeVendorRemoval( vendor ) +{ + if( vendor.aitype != 17 ) + return; + + if( ValidateObject( vendor.multi ) ) + vendor.multi.RemoveVendor( vendor ); + + vendor.Delete(); +} + +/** @param {Character} vendor @param {Character|null} notifyChar */ +function RemoveVendorForNoFunds( vendor, notifyChar ) +{ + if( vendor.aitype != 17 ) + return; + + var ownerChar = ValidateObject( vendor.owner ) ? vendor.owner : null; + if( ValidateObject( ownerChar ) && ownerChar.socket ) + { + var ownerSocket = ownerChar.socket; + ownerSocket.SysMessage( GetDictionaryEntry( 40000, ownerSocket.language ), vendor.name ); + + var deed = CreateVendorDismissDeedForOwner( ownerChar, vendor ); + if( !ValidateObject( deed )) + { + ownerSocket.SysMessage( GetDictionaryEntry( 40001, ownerSocket.language ) ); + return; + } + + var itemResults = ReturnVendorItemsToOwner( vendor, ownerChar ); + var goldResult = ReturnVendorGoldToOwner( ownerChar, vendor ); + var deedLocation = PlaceItemForOwnerOrDrop( deed, ownerChar, vendor ); + + if(( itemResults.pack + itemResults.bank + itemResults.ground ) > 0 ) + { + if( VendorSettings.OnlyReturnToBank ) + ownerSocket.SysMessage( GetDictionaryEntry( 40002, ownerSocket.language ), itemResults.bank, itemResults.ground ); + else + ownerSocket.SysMessage( GetDictionaryEntry( 40003, ownerSocket.language ), itemResults.pack, itemResults.bank, itemResults.ground ); + } + + if( goldResult.amount > 0 ) + { + if( goldResult.location == 1 ) + ownerSocket.SysMessage( GetDictionaryEntry( 40004, ownerSocket.language ), goldResult.amount ); + else if( goldResult.location == 2 ) + ownerSocket.SysMessage( GetDictionaryEntry( 40005, ownerSocket.language ), goldResult.amount ); + else if( goldResult.location == 3 ) + ownerSocket.SysMessage( GetDictionaryEntry( 40006, ownerSocket.language ), goldResult.amount ); + } + + if( deedLocation == 1 ) + ownerSocket.SysMessage( GetDictionaryEntry( 40007, ownerSocket.language ) ); + else if( deedLocation == 2 ) + ownerSocket.SysMessage( GetDictionaryEntry( 40008, ownerSocket.language ) ); + else if( deedLocation == 3 ) + ownerSocket.SysMessage( GetDictionaryEntry( 40009, ownerSocket.language ) ); + + FinalizeVendorRemoval( vendor ); + return; + } + + var itemCount = DropVendorItemsAtLocation( vendor ); + var totalGold = DropVendorGoldAtLocation( vendor ); + var offlineDeed = CreateVendorDismissDeedAtLocation( vendor ); + + if( notifyChar.socket ) + { + var notifySocket = notifyChar.socket; + notifySocket.SysMessage( GetDictionaryEntry( 40010, notifySocket.language ), vendor.name ); + + if( itemCount > 0 ) + notifySocket.SysMessage( GetDictionaryEntry( 40011, notifySocket.language ), itemCount ); + + if( totalGold > 0 ) + notifySocket.SysMessage( GetDictionaryEntry( 40006, notifySocket.language ), totalGold ); + + if( ValidateObject( offlineDeed )) + notifySocket.SysMessage( GetDictionaryEntry( 40009, notifySocket.language ) ); + } + + FinalizeVendorRemoval( vendor ); +} + +/** @param {Character} vendor @param {Character|null} notifyChar @returns {boolean} */ +function CheckVendorUpkeep( vendor, notifyChar ) +{ + if( vendor.aitype != 17 ) + return false; + + if( !VendorSettings.ChargesEnabled ) + return true; + + var chargeInterval = VendorSettings.ChargeHours * 60 * 60; + if( chargeInterval <= 0 ) + return true; + + var currentTime = Math.floor( GetCurrentClock() / 1000 ); + var lastChargeTime = vendor.GetTag( "VendorLastChargeTime" ); + + if( lastChargeTime <= 0 ) + { + vendor.SetTag( "VendorLastChargeTime", currentTime ); + return true; + } + + if( currentTime <= lastChargeTime ) + return true; + + var periodsOwed = Math.floor(( currentTime - lastChargeTime ) / chargeInterval ); + if( periodsOwed <= 0 ) + return true; + + var costPerPeriod = GetVendorChargePerPeriod( vendor ); + for( var i = 0; i < periodsOwed; ++i ) + { + if( !PayVendorCharge( vendor, costPerPeriod ) ) + { + RemoveVendorForNoFunds( vendor, notifyChar ); + return false; + } + } + + vendor.SetTag( "VendorLastChargeTime", lastChargeTime + ( periodsOwed * chargeInterval ) ); + return true; +} + +/** @param {Character} pUser @param {Character} vendor */ +function StoreVendorForGump( pUser, vendor ) +{ + if( vendor.aitype == 17 ) + pUser.SetTempTag( "PlayerVendorGumpSerial", vendor.serial ); +} + +/** @param {Character} pUser @returns {Character|null} */ +function GetStoredVendorForGump( pUser ) +{ + var serial = pUser.GetTempTag( "PlayerVendorGumpSerial" ); + if( !serial ) + return null; + + var vendor = CalcCharFromSer( serial ); + if( !ValidateObject( vendor ) || vendor.aitype != 17 ) + return null; + + return vendor; +} + +/** @param {Character} pUser @param {Character} vendor */ +function OpenVendorBackpack( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket || !ValidateObject( vendor.pack ) ) + return; + + pUser.socket.OpenContainer( vendor.pack ); +} + +/** @param {Character} pUser @returns {number} */ +function GetPackGoldAmount( pUser ) +{ + if( !ValidateObject( pUser.pack ) ) + return 0; + + var totalGold = 0; + var itemObj = pUser.pack.FirstItem(); + for( ; !pUser.pack.FinishedItems(); itemObj = pUser.pack.NextItem() ) + { + if( ValidateObject( itemObj ) && itemObj.id == 0x0EED ) + totalGold += itemObj.amount; + } + + return totalGold; +} + +/** @param {Character} pUser @param {number} amount @returns {boolean} */ +function RemoveGoldFromPack( pUser, amount ) +{ + if( amount <= 0 || !ValidateObject( pUser.pack ) ) + return false; + + if( GetPackGoldAmount( pUser ) < amount ) + return false; + + var goldStacks = []; + var itemObj = pUser.pack.FirstItem(); + for( ; !pUser.pack.FinishedItems(); itemObj = pUser.pack.NextItem() ) + { + if( ValidateObject( itemObj ) && itemObj.id == 0x0EED ) + goldStacks.push( itemObj ); + } + + for( var i = 0; i < goldStacks.length && amount > 0; ++i ) + { + if( goldStacks[i].amount <= amount ) + { + amount -= goldStacks[i].amount; + goldStacks[i].Delete(); + } + else + { + goldStacks[i].amount -= amount; + amount = 0; + } + } + + return amount <= 0; +} + +/** @type { ( pUser: Character ) => Item|null } */ +function GetBankBox( pUser ) +{ + var bankBox = pUser.FindItemLayer( 29 ); + return ValidateObject( bankBox ) ? bankBox : null; +} + +/** @param {Character} pUser @returns {boolean} */ +function CanBankReceiveVendorPayout( pUser ) +{ + var bankBox = GetBankBox( pUser ); + if( !ValidateObject( bankBox ) ) + return false; + + if( bankBox.totalItemCount >= bankBox.maxItems ) + return false; + + if( bankBox.weight > bankBox.weightMax ) + return false; + + return true; +} + +/** @param {Character} pUser @param {number} checkSize @returns {Item|null} */ +function CreateVendorBankCheck( pUser, checkSize ) +{ + if( checkSize <= 0 ) + return null; + + var bankBox = GetBankBox( pUser ); + if( !ValidateObject( bankBox )) + return null; + + var newCheck = CreateDFNItem( pUser.socket, pUser, "bankcheck", 1, "ITEM", false ); + if( !ValidateObject( newCheck )) + return null; + + newCheck.SetTag( "CheckSize", checkSize ); + newCheck.AddScriptTrigger( 5015 ); + newCheck.container = bankBox; + return newCheck; +} + +/** @param {Character} pTalking @param {Item} bankBox @param {number} depositAmt */ +function DivideDepositedGold( pTalking, bankBox, depositAmt ) +{ + var remainingGold = depositAmt; + var newGoldPile; + + while( remainingGold > 0 ) + { + if( remainingGold >= 65535 ) + { + newGoldPile = CreateDFNItem( pTalking.socket, pTalking, "0x0EED", 65535, "ITEM", false ); + remainingGold -= 65535; + } + else + { + newGoldPile = CreateDFNItem( pTalking.socket, pTalking, "0x0EED", remainingGold, "ITEM", false ); + remainingGold = 0; + } + + if( ValidateObject( newGoldPile ) ) + newGoldPile.container = bankBox; + } +} + +/** @param {Character} pUser @param {number} goldAmount @returns {boolean} */ +function DepositVendorGoldToBankBox( pUser, goldAmount ) +{ + var bankBox = GetBankBox( pUser ); + if( !ValidateObject( bankBox ) || goldAmount <= 0 ) + return false; + + if( bankBox.totalItemCount >= bankBox.maxItems ) + return false; + + if( bankBox.weight > bankBox.weightMax ) + return false; + + DivideDepositedGold( pUser, bankBox, goldAmount ); + return true; +} + +/** @type { ( pUser: Character, vendor: Character ) => void } */ +function CollectVendorGold( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + var socket = pUser.socket; + var heldGold = vendor.vendorGoldHeld || 0; + + if( heldGold <= 0 ) + { + socket.SysMessage( GetDictionaryEntry( 40012, socket.language )); + return; + } + + if( !CanBankReceiveVendorPayout( pUser )) + { + socket.SysMessage( GetDictionaryEntry( 40013, socket.language )); + return; + } + + if( heldGold > 2000 ) + { + var vendorCheck = CreateVendorBankCheck( pUser, heldGold ); + if( !ValidateObject( vendorCheck )) + { + socket.SysMessage( GetDictionaryEntry( 40014, socket.language )); + return; + } + + vendor.vendorGoldHeld = 0; + socket.SysMessage( GetDictionaryEntry( 40015, socket.language ), heldGold ); + return; + } + + if( !DepositVendorGoldToBankBox( pUser, heldGold )) + { + socket.SysMessage( GetDictionaryEntry( 40016, socket.language )); + return; + } + + vendor.vendorGoldHeld = 0; + socket.SysMessage( GetDictionaryEntry( 40017, socket.language ), heldGold ); +} + +/** @param {Character} pUser @param {Character} vendor @param {number} amount */ +function DepositToVendorBank( pUser, vendor, amount ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + var socket = pUser.socket; + if( amount <= 0 ) + { + socket.SysMessage( GetDictionaryEntry( 40018, socket.language )); + return; + } + + if( !ValidateObject( pUser.pack )) + { + socket.SysMessage( GetDictionaryEntry( 40019, socket.language )); + return; + } + + if( GetPackGoldAmount( pUser ) < amount ) + { + socket.SysMessage( GetDictionaryEntry( 40020, socket.language )); + return; + } + + var deposited = DepositVendorBankGold( vendor, amount ); + if( deposited <= 0 ) + { + socket.SysMessage( GetDictionaryEntry( 40021, socket.language )); + return; + } + + if( !RemoveGoldFromPack( pUser, deposited )) + { + WithdrawVendorBankGold( vendor, deposited ); + socket.SysMessage( GetDictionaryEntry( 40022, socket.language )); + return; + } + + socket.SysMessage( GetDictionaryEntry( 40023, socket.language ), deposited ); + + if( deposited < amount ) + socket.SysMessage( GetDictionaryEntry( 40024, socket.language )); +} + +/** @param {Character} pUser @param {Character} vendor */ +function FlushBankToHeld( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + var moved = FlushVendorBankToHeldGold( vendor ); + if( moved <= 0 ) + { + pUser.socket.SysMessage( GetDictionaryEntry( 40025, pUser.socket.language )); + return; + } + + pUser.socket.SysMessage( GetDictionaryEntry( 40026, pUser.socket.language ), moved ); +} + +/** @param {Character} pUser @param {Character} vendor */ +function DismissVendor( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + var socket = pUser.socket; + var deed = CreateVendorDismissDeedForOwner( pUser, vendor ); + if( !ValidateObject( deed )) + { + socket.SysMessage( GetDictionaryEntry( 40027, socket.language )); + return; + } + + var itemResults = ReturnVendorItemsToOwner( vendor, pUser ); + var goldResult = ReturnVendorGoldToOwner( pUser, vendor ); + var deedLocation = PlaceItemForOwnerOrDrop( deed, pUser, vendor ); + + socket.SysMessage( GetDictionaryEntry( 581, socket.language ), deed.name ); + + if(( itemResults.pack + itemResults.bank + itemResults.ground ) > 0 ) + { + if( VendorSettings.OnlyReturnToBank ) + socket.SysMessage( GetDictionaryEntry( 40002, socket.language ), itemResults.bank, itemResults.ground ); + else + socket.SysMessage( GetDictionaryEntry( 40003, socket.language ), itemResults.pack, itemResults.bank, itemResults.ground ); + } + + if( goldResult.amount > 0 ) + { + if( goldResult.location == 1 ) + socket.SysMessage( GetDictionaryEntry( 40004, socket.language ), goldResult.amount ); + else if( goldResult.location == 2 ) + socket.SysMessage( GetDictionaryEntry( 40005, socket.language ), goldResult.amount ); + else if( goldResult.location == 3 ) + socket.SysMessage( GetDictionaryEntry( 40006, socket.language ), goldResult.amount ); + } + + if( deedLocation == 1 ) + socket.SysMessage( GetDictionaryEntry( 40007, socket.language ) ); + else if( deedLocation == 2 ) + socket.SysMessage( GetDictionaryEntry( 40008, socket.language ) ); + else if( deedLocation == 3 ) + socket.SysMessage( GetDictionaryEntry( 40009, socket.language ) ); + + FinalizeVendorRemoval( vendor ); +} + +/** @param {Character} pUser @param {Character} vendor */ +function CycleVendorDirection( pUser, vendor ) +{ + if( vendor.aitype != 17 ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + vendor.direction = ( vendor.direction + 1 ) % 8; +} + +/** @param {Character} vendor @param {string} speech @returns {string} */ +function NormalizeVendorSpeech( vendor, speech ) +{ + if( !speech ) + return ""; + + var cleanedSpeech = ( "" + speech ).toLowerCase().replace( /^\s+|\s+$/g, "" ); + if( vendor.aitype != 17 || !vendor.name ) + return cleanedSpeech; + + var vendorName = ( "" + vendor.name ).toLowerCase(); + if( cleanedSpeech.indexOf( vendorName + " " ) == 0 ) + return cleanedSpeech.substring( vendorName.length + 1 ); + + if( cleanedSpeech.indexOf( vendorName + "," ) == 0 ) + return cleanedSpeech.substring( vendorName.length + 1 ).replace( /^\s+|\s+$/g, "" ); + + return cleanedSpeech; +} + +/** @param {Character} pUser @param {Character} vendor @returns {boolean} */ +function VendorAccessBlocked( pUser, vendor ) +{ + if( ValidateObject( vendor.multi ) && vendor.multi.IsOnBanList( pUser )) + { + if( pUser.socket ) + pUser.socket.SysMessage( GetDictionaryEntry( 40059, pUser.socket.language )); + return true; + } + + if( !CheckVendorUpkeep( vendor, pUser )) + return true; + + return false; +} + +/** @param {Character} pUser @param {Character} vendor @param {string} speech @returns {number} */ +function HandleVendorSpeechCommand( pUser, vendor, speech ) +{ + if( !ValidateObject( pUser ) || !ValidateObject( vendor ) || vendor.aitype != 17 ) + return 0; + + if( !pUser.InRange( vendor, 3 )) + return 0; + + var cmd = NormalizeVendorSpeech( vendor, speech ); + + if( cmd == "view" || cmd == "browse" || cmd == "look" ) + { + if( VendorAccessBlocked( pUser, vendor )) + return 2; + + vendor.TextMessage( "Take a look at my goods."); + OpenVendorBackpack( pUser, vendor ); + return 2; + } + + if( cmd == "buy" || cmd == "purchase" ) + { + if( VendorAccessBlocked( pUser, vendor )) + return 2; + + pUser.BuyFrom( vendor ); + return 2; + } + + if( cmd == "gold" || cmd == "collect" || cmd == "get" ) + { + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + { + vendor.TextMessage( GetDictionaryEntry( 40028 )); + return 2; + } + + if( !CheckVendorUpkeep( vendor, pUser )) + return 2; + + CollectVendorGold( pUser, vendor ); + return 2; + } + + if( cmd == "status" || cmd == "info" ) + { + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + { + vendor.TextMessage( GetDictionaryEntry( 40028 )); + return 2; + } + + if( !CheckVendorUpkeep( vendor, pUser )) + return 2; + + if( pUser.socket ) + { + StoreVendorForGump( pUser, vendor ); + ShowPlayerVendorOwnerGump( pUser.socket, vendor ); + } + return 2; + } + + if( cmd == "dismiss" || cmd == "replace" ) + { + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + { + vendor.TextMessage( GetDictionaryEntry( 40028 )); + return 2; + } + + DismissVendor( pUser, vendor ); + return 2; + } + + if( cmd == "vendor cycle" || cmd == "cycle" ) + { + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + { + vendor.TextMessage( GetDictionaryEntry( 40028 )); + return 2; + } + + CycleVendorDirection( pUser, vendor ); + return 2; + } + + return 0; +} + +/** @param {Character} vendor @param {number} layer */ +function RemoveVendorLayerItem( vendor, layer ) +{ + var layerItem = vendor.FindItemLayer( layer ); + if( ValidateObject( layerItem )) + layerItem.Delete(); +} + +/** @param {Character} vendor @param {number} layer @param {number} itemID @param {number} hue @param {string} itemName */ +function SetVendorLayerItem( vendor, layer, itemID, hue, itemName ) +{ + var oldItem = vendor.FindItemLayer( layer ); + if( ValidateObject( oldItem )) + oldItem.Delete(); + + if( itemID == 0 ) + return; + + var newItem = CreateBlankItem( vendor.socket ? vendor.socket : null, vendor, 1, itemName, itemID, hue, "ITEM", false ); + if( !ValidateObject( newItem )) + return; + + newItem.container = vendor; + newItem.layer = layer; +} + +/** @param {Character} vendor @param {number} layer @param {number} hue */ +function SetVendorLayerHue( vendor, layer, hue ) +{ + var layerItem = vendor.FindItemLayer( layer ); + if( ValidateObject( layerItem )) + layerItem.colour = hue; +} + +/** @param {Character} vendor @param {boolean} isElf @param {boolean} isFemale */ +function SetVendorRaceGenderBody( vendor, isElf, isFemale ) +{ + if( isElf ) + vendor.id = isFemale ? 0x025E : 0x025D; + else + vendor.id = isFemale ? 0x0191 : 0x0190; + + if( isFemale ) + RemoveVendorLayerItem( vendor, VendorEquipmentLayer.Beard ); + + vendor.Teleport(); +} + +/** @param {Character} pUser @param {number} categoryIndex */ +function StoreClothingDyeCategory( pUser, categoryIndex ) +{ + pUser.SetTempTag( "PlayerVendorClothingDyeCategory", categoryIndex ); +} + +/** @param {Character} pUser @returns {number} */ +function GetStoredClothingDyeCategory( pUser ) +{ + var categoryIndex = pUser.GetTempTag( "PlayerVendorClothingDyeCategory" ); + return ( categoryIndex === null || categoryIndex === undefined || categoryIndex === "" ) ? -1 : categoryIndex; +} + +/** @param {Socket} socket @param {Character} vendor */ +function ShowPlayerVendorOwnerGump( socket, vendor ) +{ + if( socket == null ) + return; + + if( vendor.aitype != 17 ) + return; + + var heldGold = vendor.vendorGoldHeld || 0; + var bankGold = vendor.GetTag( "VendorBankAccount" ); + if( bankGold < 0 ) + bankGold = 0; + + var perPeriod = GetVendorChargePerPeriod( vendor ); + var days = GetVendorDaysAffordable( vendor ); + var hours = GetVendorHoursAffordable( vendor ); + var itemFeeTotal = GetVendorItemFeeTotal( vendor ); + + var myGump = new Gump; + myGump.AddPage( 0 ); + myGump.AddBackground( 25, 10, 555, 240, 5054 ); + myGump.AddCheckerTrans( 32, 20, 537, 220 ); + myGump.NoResize(); + + myGump.AddButton( 390, 25, 4005, 4007, 1, 0, 1 ); + myGump.AddHTMLGump( 425, 25, 120, 20, false, false, "" + GetDictionaryEntry( 40029, socket.language ) + "" ); + + myGump.AddButton( 390, 48, 4005, 4007, 1, 0, 2 ); + myGump.AddHTMLGump( 425, 48, 120, 20, false, false, "" + GetDictionaryEntry( 40030, socket.language ) + "" ); + + myGump.AddButton( 390, 71, 4005, 4007, 1, 0, 7 ); + myGump.AddHTMLGump( 425, 72, 120, 20, false, false, "" + GetDictionaryEntry( 40048, socket.language ) + "" ); + + myGump.AddButton( 390, 94, 4005, 4007, 1, 0, 8 ); + myGump.AddHTMLGump( 425, 95, 120, 20, false, false, "" + GetDictionaryEntry( 40049, socket.language ) + "" ); + + myGump.AddButton( 390, 117, 4005, 4007, 1, 0, 3 ); + myGump.AddHTMLGump( 425, 118, 120, 20, false, false, "" + GetDictionaryEntry( 40031, socket.language ) + "" ); + + myGump.AddButton( 390, 140, 4005, 4007, 1, 0, 4 ); + myGump.AddHTMLGump( 425, 141, 120, 20, false, false, "" + GetDictionaryEntry( 40032, socket.language ) + "" ); + + myGump.AddButton( 390, 163, 4005, 4007, 1, 0, 5 ); + myGump.AddHTMLGump( 425, 164, 120, 20, false, false, "" + GetDictionaryEntry( 40033, socket.language ) + "" ); + + myGump.AddButton( 390, 186, 4005, 4007, 1, 0, 6 ); + myGump.AddHTMLGump( 425, 187, 120, 20, false, false, "" + GetDictionaryEntry( 40034, socket.language ) + "" ); + + myGump.AddButton( 390, 209, 4005, 4007, 1, 0, 0 ); + myGump.AddHTMLGump( 425, 210, 120, 20, false, false, "" + GetDictionaryEntry( 18762, socket.language ) + "" ); + + if( !VendorSettings.ChargesEnabled ) + { + myGump.AddHTMLGump( 40, 25, 260, 20, false, false, "" + GetDictionaryEntry( 40035, socket.language ) + "" ); + myGump.AddText( 300, 25, 2603, GetDictionaryEntry( 40043, socket.language ) ); + + myGump.AddHTMLGump( 40, 48, 260, 20, false, false, "" + GetDictionaryEntry( 40036, socket.language ) + "" ); + myGump.AddText( 300, 48, 2603, GetDictionaryEntry( 40043, socket.language ) ); + + myGump.AddHTMLGump( 40, 72, 260, 20, false, false, "" + GetDictionaryEntry( 40037, socket.language ) + "" ); + myGump.AddText( 300, 72, 1153, "" + heldGold ); + + myGump.AddHTMLGump( 40, 96, 260, 20, false, false, "" + GetDictionaryEntry( 40038, socket.language ) + "" ); + myGump.AddText( 300, 96, 1153, "" + bankGold ); + + myGump.AddHTMLGump( 40, 120, 260, 20, false, false, "" + GetDictionaryEntry( 40039, socket.language ) + "" ); + myGump.AddText( 300, 120, 2603, GetDictionaryEntry( 40044, socket.language ) ); + } + else + { + myGump.AddHTMLGump( 40, 25, 260, 20, false, false, "" + GetDictionaryEntry( 40035, socket.language ) + "" ); + myGump.AddText( 300, 25, 1153, "" + days ); + + myGump.AddHTMLGump( 40, 48, 260, 20, false, false, "" + GetDictionaryEntry( 40036, socket.language ) + "" ); + myGump.AddText( 300, 48, 1153, "" + hours ); + + myGump.AddHTMLGump( 40, 72, 260, 20, false, false, "" + GetDictionaryEntry( 40037, socket.language ) + "" ); + myGump.AddText( 300, 72, 1153, "" + heldGold ); + + myGump.AddHTMLGump( 40, 96, 260, 20, false, false, "" + GetDictionaryEntry( 40038, socket.language ) + "" ); + myGump.AddText( 300, 96, 1153, "" + bankGold ); + + myGump.AddHTMLGump( 40, 120, 260, 20, false, false, "" + GetDictionaryEntry( 40040, socket.language ) + "" ); + myGump.AddText( 300, 120, 1153, "" + perPeriod ); + + myGump.AddHTMLGump( 40, 144, 260, 20, false, false, "" + GetDictionaryEntry( 40041, socket.language ) + "" ); + myGump.AddText( 300, 144, 1153, "" + VendorSettings.ChargeHours ); + + myGump.AddHTMLGump( 40, 168, 260, 20, false, false, "" + GetDictionaryEntry( 40042, socket.language ) + "" ); + myGump.AddText( 300, 168, 1153, "" + itemFeeTotal ); + } + + myGump.Send( socket ); + myGump.Free(); +} + +/** @param {Socket} socket @param {Gump} myGump @param {number} menuPage @param {number} categoryPageStart @param {string} title */ +function AddHairHueCategoryMenu( socket, myGump, menuPage, categoryPageStart, title ) +{ + myGump.AddPage( menuPage ); + myGump.AddHTMLGump( 10, 42, 350, 18, false, false, "
" + title + "
" ); + + var columns = 2; + var startX = 20; + var startY = 70; + var colWidth = 160; + var rowHeight = 20; + + for( var i = 0; i < vendorHairHueCategories.length; ++i ) + { + var row = Math.floor( i / columns ); + var col = i % columns; + var x = startX + ( col * colWidth ); + var y = startY + ( row * rowHeight ); + + myGump.AddButton( x, y, 0x0A9A, 0x0A9A, 0, categoryPageStart + i, 0 ); + myGump.AddHTMLGump( x + 30, y, 125, 18, false, false, "" + GetVendorHueCategoryName( vendorHairHueCategories[i], socket ) + "" ); + } + + myGump.AddButton( 270, 340, 0xFA5, 0xFA7, 1, 1, 5007 ); + myGump.AddHTMLGump( 305, 342, 60, 18, false, false, "" + GetDictionaryEntry( 10267, socket.language ) + "" ); +} + +/** @param {Socket} socket @param {Gump} myGump @param {number} pageNumber @param {string} title @param {number[]} hueList @param {number} buttonBase @param {number} backPage */ +function AddHairHueCategoryPage( socket, myGump, pageNumber, title, hueList, buttonBase, backPage ) +{ + myGump.AddPage( pageNumber ); + myGump.AddHTMLGump( 10, 42, 350, 18, false, false, "
" + title + "
" ); + + var columns = 3; + var startX = 20; + var startY = 70; + var colWidth = 112; + var rowHeight = 18; + + for( var i = 0; i < hueList.length; ++i ) + { + var row = Math.floor( i / columns ); + var col = i % columns; + var x = startX + ( col * colWidth ); + var y = startY + ( row * rowHeight ); + var hueHex = hueList[i].toString( 16 ).toUpperCase(); + + myGump.AddButton( x, y, 0x0A9A, 0x0A9A, 1, pageNumber, buttonBase + hueList[i] ); + myGump.AddText( x + 25, y, hueList[i], "0x" + hueHex ); + } + + myGump.AddButton( 10, 340, 0xFA5, 0xFA7, 0, backPage, 0 ); + myGump.AddHTMLGump( 45, 342, 80, 18, false, false, "" + GetDictionaryEntry( 10267, socket.language ) + "" ); + + myGump.AddButton( 270, 340, 0xFA5, 0xFA7, 1, 1, 5007 ); + myGump.AddHTMLGump( 305, 342, 60, 18, false, false, "" + GetDictionaryEntry( 40045, socket.language ) + "" ); +} + +/** @param {Character} pUser @param {Character} vendor */ +function ShowVendorCustomizeGump( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + var socket = pUser.socket; + var isElf = ( vendor.id == 0x025D || vendor.id == 0x025E ); + var isFemale = ( vendor.id == 0x0191 || vendor.id == 0x025E ); + var hairStyles = humanHairStyles; + + if( isElf ) + hairStyles = isFemale ? femaleElfHairStyles : maleElfHairStyles; + + StoreVendorForGump( pUser, vendor ); + + var myGump = new Gump; + myGump.AddPage( 0 ); + myGump.AddBackground( 0, 0, 370, 370, 0x13BE ); + myGump.AddCheckerTrans( 10, 10, 350, 350 ); + myGump.AddHTMLGump( 10, 12, 350, 18, false, false, "
" + GetDictionaryEntry( 40046, socket.language ) + "
" ); + + myGump.AddPage( 1 ); + myGump.AddHTMLGump( 10, 42, 150, 18, false, false, "
" + GetDictionaryEntry( 40047, socket.language ) + "
" ); + + for( var i = 0; i < hairStyles.length; ++i ) + { + myGump.AddButton( 10, 70 + ( i * 20 ), 0xFA5, 0xFA7, 1, 1, 0x1000 + i ); + myGump.AddHTMLGump( 45, 72 + ( i * 20 ), 110, 18, false, false, "" + GetHairOrBeardName( hairStyles[i], socket ) + "" ); + } + + myGump.AddButton( 10, 70 + ( hairStyles.length * 20 ), 0xFB1, 0xFB3, 1, 1, 5002 ); + myGump.AddHTMLGump( 45, 72 + ( hairStyles.length * 20 ), 110, 18, false, false, "" + GetDictionaryEntry( 40071, socket.language ) + "" ); + + myGump.AddButton( 10, 70 + (( hairStyles.length + 1 ) * 20 ), 0xFA5, 0xFA7, 0, 2, 0 ); + myGump.AddHTMLGump( 45, 72 + (( hairStyles.length + 1 ) * 20 ), 110, 18, false, false, "" + GetDictionaryEntry( 40072, socket.language ) + "" ); + + if( !isElf && !isFemale ) + { + myGump.AddHTMLGump( 160, 42, 190, 18, false, false, "
" + GetDictionaryEntry( 40073, socket.language ) + "
" ); + + for( i = 0; i < humanBeardStyles.length; ++i ) + { + myGump.AddButton( 160, 70 + ( i * 20 ), 0xFA5, 0xFA7, 1, 1, 0x2000 + i ); + myGump.AddHTMLGump( 195, 72 + ( i * 20 ), 160, 18, false, false, "" + GetHairOrBeardName( humanBeardStyles[i], socket ) + "" ); + } + + myGump.AddButton( 160, 70 + ( humanBeardStyles.length * 20 ), 0xFB1, 0xFB3, 1, 1, 5004 ); + myGump.AddHTMLGump( 195, 72 + ( humanBeardStyles.length * 20 ), 160, 18, false, false, "" + GetDictionaryEntry( 40074, socket.language ) + "" ); + + myGump.AddButton( 160, 70 + (( humanBeardStyles.length + 1 ) * 20 ), 0xFA5, 0xFA7, 0, 100, 0 ); + myGump.AddHTMLGump( 195, 72 + (( humanBeardStyles.length + 1 ) * 20 ), 160, 18, false, false, "" + GetDictionaryEntry( 40075, socket.language ) + "" ); + } + + myGump.AddButton( 160, 290, 0xFA5, 0xFA7, 1, 1, 5001 ); + myGump.AddHTMLGump( 195, 292, 160, 18, false, false, isFemale ? "" + GetDictionaryEntry( 40076, socket.language ) + "" : "" + GetDictionaryEntry( 40077, socket.language ) + "" ); + + myGump.AddButton( 160, 310, 0xFA5, 0xFA7, 1, 1, 5006 ); + myGump.AddHTMLGump( 195, 312, 160, 18, false, false, isElf ? "" + GetDictionaryEntry( 40078, socket.language ) + "" : "" + GetDictionaryEntry( 40079, socket.language ) + "" ); + + myGump.AddButton( 160, 330, 0xFA5, 0xFA7, 1, 0, 9 ); + myGump.AddHTMLGump( 195, 332, 160, 18, false, false, "" + GetDictionaryEntry( 40080, socket.language ) + "" ); + + myGump.AddButton( 10, 340, 0xFA5, 0xFA7, 1, 1, 5000 ); + myGump.AddHTMLGump( 45, 342, 305, 18, false, false, "" + GetDictionaryEntry( 40081, socket.language ) + "" ); + + AddHairHueCategoryMenu( socket, myGump, 2, 10, GetDictionaryEntry( 40082, socket.language ) ); + AddHairHueCategoryMenu( socket, myGump, 100, 110, GetDictionaryEntry( 40083, socket.language ) ); + + for( i = 0; i < vendorHairHueCategories.length; ++i ) + { + var hairTitle = GetDictionaryEntry( 40084, socket.language ).replace( /%s/gi, GetVendorHueCategoryName( vendorHairHueCategories[i], socket ) ); + var beardTitle = GetDictionaryEntry( 40085, socket.language ).replace( /%s/gi, GetVendorHueCategoryName( vendorHairHueCategories[i], socket ) ); + + AddHairHueCategoryPage( socket, myGump, 10 + i, hairTitle, vendorHairHueCategories[i].hues, 0x3000, 2 ); + AddHairHueCategoryPage( socket, myGump, 110 + i, beardTitle, vendorHairHueCategories[i].hues, 0x4000, 100 ); + } + + myGump.Send( socket ); + myGump.Free(); +} + +/** @type { ( pUser: Character, vendor: Character, pButton: number ) => void } */ +function HandleVendorCustomizeButton( pUser, vendor, pButton ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + var isElf = ( vendor.id == 0x025D || vendor.id == 0x025E ); + var isFemale = ( vendor.id == 0x0191 || vendor.id == 0x025E ); + var hairStyles = humanHairStyles; + + if( isElf ) + hairStyles = isFemale ? femaleElfHairStyles : maleElfHairStyles; + + // Return to vendor owner menu + if( pButton == 5000 ) + { + ShowPlayerVendorOwnerGump( pUser.socket, vendor ); + return; + } + + // Refresh vendor customize menu + if( pButton == 5007 ) + { + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Toggle vendor gender + if( pButton == 5001 ) + { + RemoveVendorLayerItem( vendor, VendorEquipmentLayer.Beard ); + SetVendorRaceGenderBody( vendor, isElf, !isFemale ); + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Toggle vendor race + if( pButton == 5006 ) + { + RemoveVendorLayerItem( vendor, VendorEquipmentLayer.Hair ); + RemoveVendorLayerItem( vendor, VendorEquipmentLayer.Beard ); + SetVendorRaceGenderBody( vendor, !isElf, isFemale ); + + var newHairStyles = humanHairStyles; + var newIsElf = ( vendor.id == 0x025D || vendor.id == 0x025E ); + var newIsFemale = ( vendor.id == 0x0191 || vendor.id == 0x025E ); + + if( newIsElf ) + newHairStyles = newIsFemale ? femaleElfHairStyles : maleElfHairStyles; + + if( newHairStyles.length > 0 ) + SetVendorLayerItem( vendor, VendorEquipmentLayer.Hair, newHairStyles[0].itemID, 0, "vendor hair" ); + + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Remove vendor hair + if( pButton == 5002 ) + { + RemoveVendorLayerItem( vendor, VendorEquipmentLayer.Hair ); + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Remove vendor beard + if( pButton == 5004 ) + { + RemoveVendorLayerItem( vendor, VendorEquipmentLayer.Beard ); + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Set vendor hair style + if( pButton >= 0x1000 && pButton < ( 0x1000 + 256 ) ) + { + var hairIndex = pButton - 0x1000; + if( hairIndex >= 0 && hairIndex < hairStyles.length ) + { + var oldHairItem = vendor.FindItemLayer( VendorEquipmentLayer.Hair ); + var oldHairHue = ValidateObject( oldHairItem ) ? oldHairItem.colour : 0; + SetVendorLayerItem( vendor, VendorEquipmentLayer.Hair, hairStyles[hairIndex].itemID, oldHairHue, "vendor hair" ); + } + + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Set vendor beard style + if( pButton >= 0x2000 && pButton < ( 0x2000 + 256 ) ) + { + var beardIndex = pButton - 0x2000; + if( !isElf && !isFemale && beardIndex >= 0 && beardIndex < humanBeardStyles.length ) + { + var oldBeardItem = vendor.FindItemLayer( VendorEquipmentLayer.Beard ); + var oldBeardHue = ValidateObject( oldBeardItem ) ? oldBeardItem.colour : 0; + SetVendorLayerItem( vendor, VendorEquipmentLayer.Beard, humanBeardStyles[beardIndex].itemID, oldBeardHue, "vendor beard" ); + } + + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Set vendor hair color + if( pButton >= 0x3000 && pButton < 0x4000 ) + { + SetVendorLayerHue( vendor, VendorEquipmentLayer.Hair, pButton - 0x3000 ); + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Set vendor beard color + if( pButton >= 0x4000 && pButton < 0x5000 ) + { + if( !isElf && !isFemale ) + SetVendorLayerHue( vendor, VendorEquipmentLayer.Beard, pButton - 0x4000 ); + + ShowVendorCustomizeGump( pUser, vendor ); + } +} + +/** @param {Character} pUser @param {Character} vendor */ +function ShowVendorClothingGump( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + var socket = pUser.socket; + StoreVendorForGump( pUser, vendor ); + + var myGump = new Gump; + myGump.AddPage( 0 ); + myGump.AddBackground( 0, 0, 585, 393, 5054 ); + myGump.AddBackground( 195, 36, 387, 275, 3000 ); + myGump.AddHTMLGump( 10, 10, 565, 18, false, false, "
" + GetDictionaryEntry( 40086, socket.language ) + "
" ); + + myGump.AddHTMLGump( 60, 355, 150, 18, false, false, GetDictionaryEntry( 18718, socket.language ) ); + myGump.AddButton( 25, 355, 4005, 4007, 1, 0, 5000 ); + + myGump.AddHTMLGump( 320, 355, 150, 18, false, false, GetDictionaryEntry( 10267, socket.language ) ); + myGump.AddButton( 285, 355, 4005, 4007, 1, 0, 0x8FFF ); + + var i; + var y = 35; + for( i = 0; i < vendorClothingCategories.length; ++i ) + { + myGump.AddHTMLGump( 5, y, 150, 25, false, false, vendorClothingCategories[i].name ); + myGump.AddButton( 155, y, 4005, 4007, 0, 20 + i, 0 ); + y += 25; + } + + for( i = 0; i < vendorClothingCategories.length; ++i ) + { + var cat = vendorClothingCategories[i]; + myGump.AddPage( 20 + i ); + + for( var entryIndex = 0; entryIndex < cat.entries.length; ++entryIndex ) + { + var entry = cat.entries[entryIndex]; + var cols = cat.name == "Held Items" ? 4 : 3; + var startX = cat.name == "Held Items" ? 205 : 198; + var startY = cat.name == "Held Items" ? 42 : 38; + var colWidth = cat.name == "Held Items" ? 92 : 129; + var rowHeight = cat.name == "Held Items" ? 52 : 67; + var textWidth = cat.name == "Held Items" ? 80 : 100; + var itemOffsetX = cat.name == "Held Items" ? 18 : 20; + var itemOffsetY = cat.name == "Held Items" ? 16 : 25; + var buttonOffsetY = cat.name == "Held Items" ? 30 : 45; + + var x = startX + ( entryIndex % cols ) * colWidth; + var ey = startY + Math.floor( entryIndex / cols ) * rowHeight; + + myGump.AddHTMLGump( x, ey, textWidth, 18, false, false, entry.name ); + myGump.AddPicture( x + itemOffsetX, ey + itemOffsetY, entry.itemID ); + myGump.AddButton( x, ey + buttonOffsetY, 4005, 4007, 1, 20 + i, 0x5000 + ( i * 100 ) + entryIndex ); + } + + if( cat.canDye ) + { + myGump.AddButton( 300, 315, 4005, 4007, 1, 0, 0x8000 + i ); + myGump.AddHTMLGump( 335, 317, 100, 18, false, false, GetDictionaryEntry( 40087, socket.language ) ); + } + + myGump.AddButton( 430, 315, 4005, 4007, 1, 20 + i, 0x7000 + i ); + myGump.AddHTMLGump( 465, 317, 100, 18, false, false, GetDictionaryEntry( 40088, socket.language ) ); + } + + myGump.Send( socket ); + myGump.Free(); +} + +/** @param {Character} pUser @param {Character} vendor */ +function ShowVendorClothingDyeGump( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + var socket = pUser.socket; + var categoryIndex = GetStoredClothingDyeCategory( pUser ); + var title = GetDictionaryEntry( 40091, socket.language ); + + if( categoryIndex >= 0 && categoryIndex < vendorClothingCategories.length ) + title = vendorClothingCategories[categoryIndex].name + " COLOR"; + + StoreVendorForGump( pUser, vendor ); + + var myGump = new Gump; + myGump.AddPage( 0 ); + myGump.AddBackground( 0, 0, 370, 370, 0x13BE ); + myGump.AddCheckerTrans( 10, 10, 350, 350 ); + myGump.AddHTMLGump( 10, 12, 350, 18, false, false, "
" + title + "
" ); + + myGump.AddPage( 1 ); + myGump.AddHTMLGump( 10, 42, 350, 18, false, false, "
" + GetDictionaryEntry( 40089, socket.language ) + "
" ); + + var columns = 2; + var startX = 20; + var startY = 70; + var colWidth = 160; + var rowHeight = 20; + + for( var i = 0; i < vendorHairHueCategories.length; ++i ) + { + var row = Math.floor( i / columns ); + var col = i % columns; + var x = startX + ( col * colWidth ); + var y = startY + ( row * rowHeight ); + + myGump.AddButton( x, y, 0x0A9A, 0x0A9A, 0, 10 + i, 0 ); + myGump.AddHTMLGump( x + 30, y, 125, 18, false, false, "" + GetVendorHueCategoryName( vendorHairHueCategories[i], socket ) + "" ); + } + + myGump.AddButton( 10, 340, 0xFA5, 0xFA7, 1, 0, 0x8FFE ); + myGump.AddHTMLGump( 45, 342, 120, 18, false, false, "" + GetDictionaryEntry( 10267, socket.language ) + "" ); + + for( i = 0; i < vendorHairHueCategories.length; ++i ) + { + myGump.AddPage( 10 + i ); + myGump.AddHTMLGump( 10, 42, 350, 18, false, false, "
" + GetVendorHueCategoryName( vendorHairHueCategories[i], socket ) + "
" ); + + var hues = vendorHairHueCategories[i].hues; + for( var j = 0; j < hues.length; ++j ) + { + var hueRow = Math.floor( j / 3 ); + var hueCol = j % 3; + var hx = 20 + ( hueCol * 112 ); + var hy = 70 + ( hueRow * 18 ); + var hueHex = hues[j].toString( 16 ).toUpperCase(); + + myGump.AddButton( hx, hy, 0x0A9A, 0x0A9A, 1, 10 + i, 0x9000 + hues[j] ); + myGump.AddText( hx + 25, hy, hues[j], "0x" + hueHex ); + } + + myGump.AddButton( 10, 340, 0xFA5, 0xFA7, 0, 1, 0 ); + myGump.AddHTMLGump( 45, 342, 80, 18, false, false, "" + GetDictionaryEntry( 10267, socket.language ) + "" ); + + myGump.AddButton( 270, 340, 0xFA5, 0xFA7, 1, 0, 0x8FFE ); + myGump.AddHTMLGump( 305, 342, 60, 18, false, false, "" + GetDictionaryEntry( 40090, socket.language ) + "" ); + } + + myGump.Send( socket ); + myGump.Free(); +} + +/** @param {Character} pUser @param {Character} vendor @param {number} pButton */ +function HandleVendorClothingButton( pUser, vendor, pButton ) +{ + if( vendor.aitype != 17 ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + // Return to vendor customize menu + if( pButton == 0x8FFF ) + { + ShowVendorCustomizeGump( pUser, vendor ); + return; + } + + // Return to vendor clothing menu + if( pButton == 0x8FFE ) + { + ShowVendorClothingGump( pUser, vendor ); + return; + } + + // Remove selected clothing layer + if( pButton >= 0x7000 && pButton < ( 0x7000 + 100 ) ) + { + var removeCatIndex = pButton - 0x7000; + if( removeCatIndex >= 0 && removeCatIndex < vendorClothingCategories.length ) + { + RemoveVendorLayerItem( vendor, vendorClothingCategories[removeCatIndex].layer ); + ShowVendorClothingGump( pUser, vendor ); + } + return; + } + + // Equip selected clothing item + if( pButton >= 0x5000 && pButton < ( 0x5000 + 10000 ) ) + { + var entryValue = pButton - 0x5000; + var catIndex = Math.floor( entryValue / 100 ); + var entryIndex = entryValue % 100; + + if( catIndex >= 0 && catIndex < vendorClothingCategories.length ) + { + var cat = vendorClothingCategories[catIndex]; + if( entryIndex >= 0 && entryIndex < cat.entries.length ) + { + var oldClothingItem = vendor.FindItemLayer( cat.layer ); + var oldClothingHue = ValidateObject( oldClothingItem ) ? oldClothingItem.colour : 0; + SetVendorLayerItem( vendor, cat.layer, cat.entries[entryIndex].itemID, oldClothingHue, "vendor clothing" ); + ShowVendorClothingGump( pUser, vendor ); + } + } + return; + } + + // Open clothing dye menu + if( pButton >= 0x8000 && pButton < ( 0x8000 + vendorClothingCategories.length ) ) + { + var dyeCatIndex = pButton - 0x8000; + if( dyeCatIndex >= 0 && dyeCatIndex < vendorClothingCategories.length ) + { + StoreClothingDyeCategory( pUser, dyeCatIndex ); + ShowVendorClothingDyeGump( pUser, vendor ); + } + return; + } + + // Set selected clothing color + if( pButton >= 0x9000 && pButton < ( 0x9000 + 0x1000 ) ) + { + var hueValue = pButton - 0x9000; + var storedCatIndex = GetStoredClothingDyeCategory( pUser ); + + if( storedCatIndex >= 0 && storedCatIndex < vendorClothingCategories.length ) + { + var dyeCat = vendorClothingCategories[storedCatIndex]; + if( dyeCat.canDye ) + SetVendorLayerHue( vendor, dyeCat.layer, hueValue ); + } + + ShowVendorClothingDyeGump( pUser, vendor ); + } +} + +/** @param {string} text @returns {string} */ +function TrimString( text ) +{ + if( text === null || text === undefined ) + return ""; + + return ( "" + text ).replace( /^\s+|\s+$/g, "" ); +} + +/** @param {Character} pUser @param {Character} vendor */ +function BeginRenameVendor( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + StoreVendorForGump( pUser, vendor ); + pUser.SpeechInput( 20, vendor ); + pUser.socket.SysMessage( GetDictionaryEntry( 40050, pUser.socket.language ) ); +} + +/** @param {Character} pUser @param {Character} vendor */ +function BeginRenameVendorShop( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + StoreVendorForGump( pUser, vendor ); + pUser.SpeechInput( 21, vendor ); + pUser.socket.SysMessage( GetDictionaryEntry( 40051, pUser.socket.language ) ); +} + +/** @param {Character} pUser @param {Character} vendor */ +function BeginDepositVendorGold( pUser, vendor ) +{ + if( vendor.aitype != 17 || !pUser.socket ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + StoreVendorForGump( pUser, vendor ); + pUser.SpeechInput( 22, vendor ); + pUser.socket.SysMessage( GetDictionaryEntry( 40052, pUser.socket.language ) ); +} + +/** @type { ( pUser: Character, targChar: Character, nonMouseClickEvent: boolean ) => boolean } */ +function onCharDoubleClick( pUser, targChar, nonMouseClickEvent ) +{ + if( !ValidateObject( pUser ) || !ValidateObject( targChar ) ) + return false; + + if( nonMouseClickEvent ) + return true; + + if( targChar.aitype != 17 ) + return true; + + if( !CheckVendorUpkeep( targChar, pUser ) ) + return false; + + if( ValidateObject( targChar.owner ) && targChar.owner.serial == pUser.serial ) + { + if( pUser.socket ) + { + StoreVendorForGump( pUser, targChar ); + ShowPlayerVendorOwnerGump( pUser.socket, targChar ); + } + return false; + } + + if( ValidateObject( targChar.multi ) && targChar.multi.IsOnBanList( pUser ) ) + { + if( pUser.socket ) + pUser.socket.SysMessage( GetDictionaryEntry( 40059, pUser.socket.language ) ); + return false; + } + + OpenVendorBackpack( pUser, targChar ); + return false; +} + +/** @param {string} speech @param {Character} personTalking @param {BaseObject} talkingTo @returns {number} */ +function onSpeech( speech, personTalking, talkingTo ) +{ + if( !ValidateObject( personTalking ) || !ValidateObject( talkingTo ) ) + return 0; + + if( talkingTo.aitype != 17 ) + return 0; + + return HandleVendorSpeechCommand( personTalking, talkingTo, speech ); +} + +/** @param {Socket} pSock @param {number} pButton @param {GumpData} gumpData */ +function onGumpPress( pSock, pButton, gumpData ) +{ + var pUser = pSock.currentChar; + if( !ValidateObject( pUser ) ) + return; + + var vendor = GetStoredVendorForGump( pUser ); + if( !ValidateObject( vendor ) || vendor.aitype != 17 ) + return; + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + return; + + if( !CheckVendorUpkeep( vendor, pUser ) ) + return; + + // Handle vendor customize buttons + if( pButton == 5000 || pButton == 5001 || pButton == 5002 || pButton == 5004 || pButton == 5006 || pButton == 5007 || + ( pButton >= 0x1000 && pButton < ( 0x1000 + 256 ) ) || + ( pButton >= 0x2000 && pButton < ( 0x2000 + 256 ) ) || + ( pButton >= 0x3000 && pButton < 0x4000 ) || + ( pButton >= 0x4000 && pButton < 0x5000 ) ) + { + HandleVendorCustomizeButton( pUser, vendor, pButton ); + return; + } + + // Handle vendor clothing buttons + if( pButton == 0x8FFF || pButton == 0x8FFE || + ( pButton >= 0x5000 && pButton < ( 0x5000 + 10000 ) ) || + ( pButton >= 0x7000 && pButton < ( 0x7000 + 100 ) ) || + ( pButton >= 0x8000 && pButton < ( 0x8000 + vendorClothingCategories.length ) ) || + ( pButton >= 0x9000 && pButton < ( 0x9000 + 0x1000 ) ) ) + { + HandleVendorClothingButton( pUser, vendor, pButton ); + return; + } + + switch( pButton ) + { + case 0: break; + case 1: OpenVendorBackpack( pUser, vendor ); break; + case 2: ShowVendorCustomizeGump( pUser, vendor ); break; + case 3: + CollectVendorGold( pUser, vendor ); + ShowPlayerVendorOwnerGump( pSock, vendor ); + break; + case 4: BeginDepositVendorGold( pUser, vendor ); break; + case 5: + FlushBankToHeld( pUser, vendor ); + ShowPlayerVendorOwnerGump( pSock, vendor ); + break; + case 6: DismissVendor( pUser, vendor ); break; + case 7: BeginRenameVendor( pUser, vendor ); break; + case 8: BeginRenameVendorShop( pUser, vendor ); break; + case 9: ShowVendorClothingGump( pUser, vendor ); break; + } +} + +/** @type { ( pUser: Character, targObj: BaseObject, pSpeech: string, pSpeechID: number ) => void } */ +function onSpeechInput( pUser, targObj, pSpeech, pSpeechID ) +{ + if( !ValidateObject( pUser ) || !pUser.socket ) + return; + + var socket = pUser.socket; + var vendor = GetStoredVendorForGump( pUser ); + if( !ValidateObject( vendor ) || vendor.aitype != 17 ) + { + socket.SysMessage( GetDictionaryEntry( 40053, socket.language ) ); + return; + } + + if( !ValidateObject( vendor.owner ) || vendor.owner.serial != pUser.serial ) + { + socket.SysMessage( GetDictionaryEntry( 40028, socket.language ) ); + return; + } + + if( !CheckVendorUpkeep( vendor, pUser ) ) + return; + + var newText = TrimString( pSpeech ); + if( newText == "" ) + { + socket.SysMessage( GetDictionaryEntry( 9270, socket.language ) ); + return; + } + + switch( pSpeechID ) + { + case 20: + if( newText.length > 50 ) + { + socket.SysMessage( GetDictionaryEntry( 9271, socket.language ) ); + return; + } + + vendor.name = newText; + vendor.Refresh(); + socket.SysMessage( GetDictionaryEntry( 40054, socket.language ), vendor.name ); + ShowPlayerVendorOwnerGump( socket, vendor ); + break; + + case 21: + if( newText.length > 40 ) + { + socket.SysMessage( GetDictionaryEntry( 40055, socket.language ) ); + return; + } + + vendor.title = "Shop Name: " + newText; + vendor.Refresh(); + socket.SysMessage( GetDictionaryEntry( 40056, socket.language ), vendor.title ); + ShowPlayerVendorOwnerGump( socket, vendor ); + break; + + case 22: + if( !/^\d+$/.test( newText ) ) + { + socket.SysMessage( GetDictionaryEntry( 40057, socket.language ) ); + return; + } + + var depositAmount = parseInt( newText, 10 ); + if( isNaN( depositAmount ) || depositAmount <= 0 ) + { + socket.SysMessage( GetDictionaryEntry( 40018, socket.language ) ); + return; + } + + DepositToVendorBank( pUser, vendor, depositAmount ); + ShowPlayerVendorOwnerGump( socket, vendor ); + break; + } +} + +/** @type { ( timerObj: BaseObject, timerID: number ) => void } */ +function onTimer( timerObj, timerID ) +{ + if( timerID != VendorTimer.Upkeep ) + return; + + if( !ValidateObject( timerObj ) || !timerObj.isChar || timerObj.aitype != 17 ) + return; + + if( CheckVendorUpkeep( timerObj, null ) ) + StartVendorUpkeepTimer( timerObj ); +} + + +function StartVendorUpkeepTimer( vendor ) +{ + if( vendor.aitype != 17 ) + return; + + vendor.StartTimer( 60000, VendorTimer.Upkeep, true ); +} \ No newline at end of file diff --git a/data/js/server/house/playerVendorDeed.js b/data/js/server/house/playerVendorDeed.js index 47e286204..d52a19537 100644 --- a/data/js/server/house/playerVendorDeed.js +++ b/data/js/server/house/playerVendorDeed.js @@ -9,9 +9,9 @@ function onUseChecked( pUser, iUsed ) if( pSocket != null && ValidateObject( iUsed ) && iUsed.isItem ) { var itemOwner = GetPackOwner( iUsed, 0 ); - if( itemOwner == null || itemOwner.serial != pUser.serial ) + if( itemOwner == null || itemOwner.serial != pUser.serial || !ValidateObject( iUsed.container) || iUsed.container.layer == 0x1D ) { - pUser.SysMessage( GetDictionaryEntry( 1763, pSocket.language )); // That item must be in your backpack before it can be used. + pSocket.SysMessage( GetDictionaryEntry( 1763, pSocket.language )); // That item must be in your backpack before it can be used. return false; } @@ -20,7 +20,7 @@ function onUseChecked( pUser, iUsed ) if( !ValidateObject( iMulti ) || !iMulti.IsInMulti( pUser )) { // Player vendors can only be placed in houses! - pUser.SysMessage( GetDictionaryEntry( 2857, pSocket.language )); // Player vendors can only be placed in houses! + pSocket.SysMessage( GetDictionaryEntry( 2857, pSocket.language )); // Player vendors can only be placed in houses! return false; } @@ -28,7 +28,7 @@ function onUseChecked( pUser, iUsed ) if( !iMulti.IsOwner( pUser )) { // Only the house owner can place player vendors in a house! - pUser.SysMessage( GetDictionaryEntry( 2858, pSocket.language )); // Only the house owner can place player vendors in a house! + pSocket.SysMessage( GetDictionaryEntry( 2858, pSocket.language )); // Only the house owner can place player vendors in a house! return false; } @@ -36,7 +36,7 @@ function onUseChecked( pUser, iUsed ) if( iMulti.vendors >= iMulti.maxVendors ) { // You cannot place any more player vendors in this house! - pUser.SysMessage( GetDictionaryEntry( 2859, pSocket.language )); // You cannot place any more player vendors in this house! + pSocket.SysMessage( GetDictionaryEntry( 2859, pSocket.language )); // You cannot place any more player vendors in this house! return false; } @@ -56,6 +56,14 @@ function onUseChecked( pUser, iUsed ) return false; } + // Make sure there is not already an NPC on the exact tile + var foundNPC = AreaCharacterFunction( "CheckForNPCAtVendorSpot", pUser, 0, pSocket ); + if( foundNPC ) + { + pSocket.SysMessage( "You cannot place a player vendor on top of another NPC." ); + return false; + } + // Create player vendor at player's current location var npcVendor = SpawnNPC( "playervendor", pUser.x, pUser.y, pUser.z, pUser.worldnumber, pUser.instanceID ); if( ValidateObject( npcVendor )) @@ -72,6 +80,14 @@ function onUseChecked( pUser, iUsed ) // Add the player vendor to the house's list of vendors iMulti.AddVendor( npcVendor ); + npcVendor.SetTag( "VendorBankAccount",0 ); + npcVendor.AddScriptTrigger( 3110 ); + npcVendor.StartTimer( 60000, 90, 3110 ); + var vendorName = iUsed.GetTag( "vendorName" ); + if( vendorName ) + { + npcVendor.name = vendorName; + } // Delete the player vendor deed! iUsed.Delete(); @@ -85,6 +101,23 @@ function onUseChecked( pUser, iUsed ) return false; } +/** @type { ( srcChar: Character, trgChar: Character, pSocket: Socket ) => boolean } */ +function CheckForNPCAtVendorSpot( srcChar, trgChar, pSocket ) +{ + if( !ValidateObject( srcChar ) || !ValidateObject( trgChar )) + return false; + + // Ignore the player placing the vendor + if( trgChar.serial == srcChar.serial ) + return false; + + // Only block NPCs already standing on the exact placement tile + if( trgChar.npc && trgChar.x == srcChar.x && trgChar.y == srcChar.y && trgChar.z == srcChar.z ) + return true; + + return false; +} + function CheckForNearbyDoors( pUser, itemToCheck, pSocket ) { if( ValidateObject( itemToCheck )) @@ -107,11 +140,11 @@ function CheckForNearbyDoors( pUser, itemToCheck, pSocket ) // Make sure to check against the distance from the door in it's closed state, rather than it's open state! var origX = itemToCheck.x - itemToCheck.GetTag( "DOOR_X" ); var origY = itemToCheck.y - itemToCheck.GetTag( "DOOR_Y" ); - if( pUser.x - origX < 2 || origX - pUser.x < 2 || pUser.y - origY < 2 || origY - pUser.y < 2 ) - return true; + if( Math.abs( pUser.x - origX ) < 1 && Math.abs( pUser.y - origY ) < 1 ) + return true; } - if( pUser.DistanceTo( itemToCheck ) <= 2 ) + if( pUser.DistanceTo( itemToCheck ) <= 1 ) return true; } } diff --git a/data/uox.ini b/data/uox.ini index ec78aaded..5e3e0ca9e 100644 --- a/data/uox.ini +++ b/data/uox.ini @@ -243,6 +243,18 @@ PETLOYALTYRATE=900 PETBONDINGENABLED=0 } +[playervendor] +{ +VENDORCHARGESENABLED=1 +VENDORBASECHARGE=60 +VENDORCHARGEHOURS=24 +VENDORUSEITEMFEESENABLED=1 +VENDORITEMFEEDIVISOR=500 +VENDORITEMFEEAMOUNT=3 +ONLYRETURNTOBANK=1 +VENDORMAXFUNDS=16960 +} + [speedup] { CHECKITEMS=1.5 diff --git a/docs/jsdocs.html b/docs/jsdocs.html index 4b185c73a..455d630d3 100644 --- a/docs/jsdocs.html +++ b/docs/jsdocs.html @@ -16716,6 +16716,10 @@

Integer Types

.visible (int, 0 - 3)
Get/Set visibility of character to other players (0 = Visible, 1 = Hidden, 2 = Magically Invisible, 3 = Visible to GMs only)

+
+

.vendorGoldHeld (int)
+ Get/Set amount of gold the vendor is holding after a player buys an item.

+

.wandertype (int, 0 - 3)
Get/Set wandertype for NPC (0 = None, [1 = Follow], 2 = Free, 3 = Box, 4 = Circle, 5 = Frozen, [6 = Flee, 7 = Pathfind]. Types 1, 6 and 7 are set primarily by code)

diff --git a/source/CResponse.cpp b/source/CResponse.cpp index 96031ceff..118e8616e 100644 --- a/source/CResponse.cpp +++ b/source/CResponse.cpp @@ -242,13 +242,6 @@ bool WhichResponse( CSocket *mSock, CChar *mChar, std::string text, CChar *tChar case TW_BUY: tResp = new CVendorBuyResponse( false, oldstrutil::upper( text )); break; case TW_VENDORSELL: tResp = new CVendorSellResponse( true, oldstrutil::upper( text )); break; case TW_SELL: tResp = new CVendorSellResponse( false, oldstrutil::upper( text )); break; - case TW_VENDORVIEW: tResp = new CVendorViewResponse( true, oldstrutil::upper( text )); break; - case TW_VIEW: tResp = new CVendorViewResponse( false, oldstrutil::upper( text )); break; - case TW_VENDORGOLD: tResp = new CVendorGoldResponse( true, oldstrutil::upper( text )); break; - case TW_COLLECT: [[fallthrough]]; - case TW_GOLD: tResp = new CVendorGoldResponse( false, oldstrutil::upper( text )); break; - case TW_VENDORSTATUS: tResp = new CVendorStatusResponse( true, oldstrutil::upper( text )); break; - case TW_STATUS: tResp = new CVendorStatusResponse( false, oldstrutil::upper( text )); break; case TW_VENDORDISMISS: tResp = new CVendorDismissResponse( true, oldstrutil::upper( text )); break; case TW_DISMISS: tResp = new CVendorDismissResponse( false, oldstrutil::upper( text )); break; case TW_HOUSEBAN: tResp = new CHouseMultiResponse( TARGET_HOUSEBAN, 585 ); break; @@ -1032,144 +1025,6 @@ bool CVendorSellResponse::Handle( CSocket *mSock, CChar *mChar, CChar *vendorNpc return false; } -CVendorViewResponse::CVendorViewResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text ) -{ -} -//o------------------------------------------------------------------------------------------------o -//| Function - CVendorViewResponse::Handle() -//o------------------------------------------------------------------------------------------------o -//| Purpose - Handles response to players wanting to view the items a vendor has for sale -//o------------------------------------------------------------------------------------------------o -bool CVendorViewResponse::Handle( CSocket *mSock, [[maybe_unused]] CChar *mChar, CChar *vendorNpc ) -{ - if( vendorNpc->GetNpcAiType() == AI_PLAYERVENDOR ) - { - CItem *pack = nullptr; - vendorNpc->TextMessage( mSock, 385, TALK, false ); // Take a look at my goods. - pack = vendorNpc->GetPackItem(); - if( ValidateObject( pack )) - { - mSock->OpenPack( pack, true ); - } - } - if( !saidVendor ) - return false; - - return true; -} - -CVendorGoldResponse::CVendorGoldResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text ) -{ -} -//o------------------------------------------------------------------------------------------------o -//| Function - CVendorGoldResponse::Handle() -//o------------------------------------------------------------------------------------------------o -//| Purpose - Handles response to players wanting to withdraw their earnings from their vendor -//o------------------------------------------------------------------------------------------------o -bool CVendorGoldResponse::Handle( CSocket *mSock, [[maybe_unused]] CChar *mChar, CChar *vendorNpc ) -{ - if( vendorNpc->GetNpcAiType() == AI_PLAYERVENDOR ) - { - CChar *mChar = mSock->CurrcharObj(); - if( mChar == vendorNpc->GetOwnerObj() ) - { - UI32 pay = 0; - auto give = vendorNpc->GetHoldG(); - auto earned = vendorNpc->GetHoldG(); - - if( vendorNpc->GetHoldG() <= 0 ) - { - vendorNpc->TextMessage( mSock, 1326, TALK, false ); // I have no gold waiting for you. - vendorNpc->SetHoldG( 0 ); - } - else if( vendorNpc->GetHoldG() > 0 && vendorNpc->GetHoldG() <= MAX_STACK ) - { - if( vendorNpc->GetHoldG() > 9 ) - { - pay = static_cast( vendorNpc->GetHoldG() / 10 ); - give = vendorNpc->GetHoldG() - pay; - } - else - { - pay = vendorNpc->GetHoldG(); - give = 0; - } - vendorNpc->SetHoldG( 0 ); - } - else - { - UI32 t = vendorNpc->GetHoldG() - MAX_STACK; // yank of 65 grand, then do calculations - vendorNpc->SetHoldG( t ); - pay = 6554; - give = 58981; - if( t > 0 ) - { - vendorNpc->TextMessage( mSock, 1327, TALK, false ); // You still have money left to claim. - } - } - if( give ) - { - Items->CreateScriptItem( mSock, mChar, "0x0EED", give, OT_ITEM, true ); - } - - // Today's purchases total %i gold. I am keeping %i gold for my self. Here is the remaining %i gold. Have a nice day. - vendorNpc->TextMessage( mSock, 1328, TALK, 0, earned, pay, give ); - } - else - { - vendorNpc->TextMessage( mSock, 1329, TALK, false ); // I don't work for you! - } - } - if( !saidVendor ) - return false; - - return true; -} - -CVendorStatusResponse::CVendorStatusResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text ) -{ -} -//o------------------------------------------------------------------------------------------------o -//| Function - CVendorStatusResponse::Handle() -//o------------------------------------------------------------------------------------------------o -//| Purpose - Handles response to players wanting to see the status of their vendor -//o------------------------------------------------------------------------------------------------o -bool CVendorStatusResponse::Handle( CSocket *mSock, [[maybe_unused]] CChar *mChar, CChar *vendorNpc ) -{ - if( vendorNpc->GetNpcAiType() == AI_PLAYERVENDOR ) - { - CChar *mChar = mSock->CurrcharObj(); - if( mChar == vendorNpc->GetOwnerObj() ) - { - if( vendorNpc->GetHoldG() <= 0 ) - { - vendorNpc->TextMessage( mSock, 1326, TALK, false ); // I have no gold waiting for you. - } - else - { - UI32 pay = 0; - if( vendorNpc->GetHoldG() > 9 ) - { - pay = static_cast( vendorNpc->GetHoldG() / 10 ); - } - else - { - pay = vendorNpc->GetHoldG(); - } - vendorNpc->TextMessage( mSock, 1782, TALK, 0, vendorNpc->GetHoldG(), pay ); // Today's purchases total %i gold. I am keeping %i gold for my self. - } - } - else - { - vendorNpc->TextMessage( mSock, 1329, TALK, false ); // I don't work for you! - } - } - if( !saidVendor ) - return false; - - return true; -} - CVendorDismissResponse::CVendorDismissResponse( bool vendVal, const std::string &text ) : CBaseVendorResponse( vendVal, text ) { } diff --git a/source/CResponse.h b/source/CResponse.h index b1434f29b..f36095784 100644 --- a/source/CResponse.h +++ b/source/CResponse.h @@ -291,36 +291,6 @@ class CVendorSellResponse : public CBaseVendorResponse virtual bool Handle( CSocket *mSock, CChar *mChar, CChar *Npc ) override; }; -class CVendorViewResponse : public CBaseVendorResponse -{ -public: - CVendorViewResponse( bool vendVal, const std::string &text ); - virtual ~CVendorViewResponse() - { - } - virtual bool Handle( CSocket *mSock, CChar *mChar, CChar *Npc ) override; -}; - -class CVendorGoldResponse : public CBaseVendorResponse -{ -public: - CVendorGoldResponse( bool vendVal, const std::string &text ); - virtual ~CVendorGoldResponse() - { - } - virtual bool Handle( CSocket *mSock, CChar *mChar, CChar *Npc ) override; -}; - -class CVendorStatusResponse : public CBaseVendorResponse -{ -public: - CVendorStatusResponse( bool vendVal, const std::string &text ); - virtual ~CVendorStatusResponse() - { - } - virtual bool Handle( CSocket *mSock, CChar *mChar, CChar *Npc ) override; -}; - class CVendorDismissResponse : public CBaseVendorResponse { public: diff --git a/source/SEFunctions.cpp b/source/SEFunctions.cpp index 84a67872a..562a3a5fb 100644 --- a/source/SEFunctions.cpp +++ b/source/SEFunctions.cpp @@ -5775,7 +5775,31 @@ JSBool SE_GetServerSetting( JSContext *cx, uintN argc, jsval *vp ) JS_SET_RVAL( cx, vp, BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->EventManagerSystem() )); break; case 407: // LOGINQUESTENABLED - JS_SET_RVAL( cx, vp, BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->LoginQuestEnabled() ) ); + JS_SET_RVAL( cx, vp, BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->LoginQuestEnabled() )); + break; + case 408: // VENDORCHARGESENABLED + JS_SET_RVAL( cx, vp, BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->VendorChargesEnabled() )); + break; + case 409: // VENDORBASECHARGE + JS_SET_RVAL( cx, vp, INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->VendorBaseCharge() ))); + break; + case 410: // VENDORCHARGEHOURS + JS_SET_RVAL( cx, vp, INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->VendorChargeHours() ))); + break; + case 411: // VENDORUSEITEMFEESENABLED + JS_SET_RVAL( cx, vp, BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->VendorUseItemFeesEnabled() )); + break; + case 412: // VENDORITEMFEEDIVISOR + JS_SET_RVAL( cx, vp, INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->VendorItemFeeDivisor() ))); + break; + case 413: // VENDORITEMFEEAMOUNT + JS_SET_RVAL( cx, vp, INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->VendorItemFeeAmount() ))); + break; + case 414: // ONLYRETURNTOBANK + JS_SET_RVAL( cx, vp, BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->OnlyReturnToBank() )); + break; + case 415: // VENDORMAXFUNDS + JS_SET_RVAL( cx, vp, INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->VendorMaxFunds() ))); break; default: ScriptError( cx, "GetServerSetting: Invalid server setting name provided" ); diff --git a/source/UOXJSPropertyEnums.h b/source/UOXJSPropertyEnums.h index 7a09cc7ec..b9b27c020 100644 --- a/source/UOXJSPropertyEnums.h +++ b/source/UOXJSPropertyEnums.h @@ -365,6 +365,7 @@ enum CC_Properties CCP_AITYPE, CCP_SPLIT, CCP_SPLITCHANCE, + CCP_VENDORGOLDHELD, CCP_TRAINER, CCP_HIRELING, CCP_WEIGHT, diff --git a/source/UOXJSPropertyFuncs.cpp b/source/UOXJSPropertyFuncs.cpp index eed6afb31..1894ab6d9 100644 --- a/source/UOXJSPropertyFuncs.cpp +++ b/source/UOXJSPropertyFuncs.cpp @@ -2176,6 +2176,7 @@ JSBool CCharacterProps_getProperty( JSContext *cx, JSObject *obj, jsid id, jsval case CCP_AITYPE: *vp = INT_TO_JSVAL( gPriv->GetNpcAiType() ); break; case CCP_SPLIT: *vp = INT_TO_JSVAL( gPriv->GetSplit() ); break; case CCP_SPLITCHANCE: *vp = INT_TO_JSVAL( gPriv->GetSplitChance() ); break; + case CCP_VENDORGOLDHELD:*vp = INT_TO_JSVAL( gPriv->GetHoldG() ); break; case CCP_TRAINER: *vp = BOOLEAN_TO_JSVAL( gPriv->CanTrain() ); break; case CCP_HIRELING: *vp = BOOLEAN_TO_JSVAL( gPriv->CanBeHired() ); break; case CCP_WEIGHT: *vp = INT_TO_JSVAL( gPriv->GetWeight() ); break; @@ -2694,6 +2695,7 @@ JSBool CCharacterProps_setProperty( JSContext* cx, JSObject* obj, jsid id, JSBoo case CCP_AITYPE: gPriv->SetNPCAiType( static_cast( encaps.toInt() )); break; case CCP_SPLIT: gPriv->SetSplit( static_cast( encaps.toInt() )); break; case CCP_SPLITCHANCE: gPriv->SetSplitChance( static_cast( encaps.toInt() ));break; + case CCP_VENDORGOLDHELD:gPriv->SetHoldG( static_cast( encaps.toInt() )); break; case CCP_HIRELING: gPriv->SetCanHire( encaps.toBool() ); break; case CCP_TRAINER: gPriv->SetCanTrain( encaps.toBool() ); break; case CCP_WEIGHT: gPriv->SetWeight( static_cast( encaps.toInt() )); break; diff --git a/source/UOXJSPropertySpecs.h b/source/UOXJSPropertySpecs.h index d274d5df3..d917f3fe8 100644 --- a/source/UOXJSPropertySpecs.h +++ b/source/UOXJSPropertySpecs.h @@ -384,6 +384,7 @@ inline JSPropertySpec CCharacterProps[] = { "aitype", CCP_AITYPE, JSPROP_ENUMANDPERM, nullptr, nullptr }, { "split", CCP_SPLIT, JSPROP_ENUMANDPERM, nullptr, nullptr }, { "splitchance", CCP_SPLITCHANCE, JSPROP_ENUMANDPERM, nullptr, nullptr }, + { "vendorGoldHeld", CCP_VENDORGOLDHELD, JSPROP_ENUMANDPERM, nullptr, nullptr }, { "hireling", CCP_HIRELING, JSPROP_ENUMANDPERM, nullptr, nullptr }, { "trainer", CCP_TRAINER, JSPROP_ENUMANDPERM, nullptr, nullptr }, { "weight", CCP_WEIGHT, JSPROP_ENUMANDPERM, nullptr, nullptr }, diff --git a/source/cServerData.cpp b/source/cServerData.cpp index bea28892b..0750efdc5 100644 --- a/source/cServerData.cpp +++ b/source/cServerData.cpp @@ -419,6 +419,14 @@ const std::map CServerData::uox3IniCaseValue {"SPEEDHACKTHROTTLEPENALTY", 405}, {"EVENTMANAGERSYSTEM", 406}, {"LOGINQUESTENABLED"s, 407}, + {"VENDORCHARGESENABLED", 408}, + {"VENDORBASECHARGE", 409}, + {"VENDORCHARGEHOURS", 410}, + {"VENDORUSEITEMFEESENABLED", 411}, + {"VENDORITEMFEEDIVISOR", 412}, + {"VENDORITEMFEEAMOUNT", 413}, + {"ONLYRETURNTOBANK", 414}, + {"VENDORMAXFUNDS", 415}, }; constexpr auto MAX_TRACKINGTARGETS = 128; constexpr auto SKILLTOTALCAP = 7000; @@ -540,8 +548,11 @@ constexpr auto BIT_HOUSEITEMSDELETEONDECAY = UI32( 112 ); constexpr auto BIT_HOUSEGRANDFATHERED = UI32( 113 ); constexpr auto BIT_SPEEDHACKDETECTION = UI32( 119 ); constexpr auto BIT_EVENTMANAGERSYSTEM = UI32( 120 ); -constexpr auto BIT_QUESTSYSTEMENABLED = UI32( 121 ); -constexpr auto BIT_LOGINQUESTENABLED = UI32( 122 ); +constexpr auto BIT_QUESTSYSTEMENABLED = UI32( 121 ); +constexpr auto BIT_LOGINQUESTENABLED = UI32( 122 ); +constexpr auto BIT_VENDORCHARGESENABLED = UI32( 123 ); +constexpr auto BIT_VENDORUSEITEMFEESENABLED = UI32( 124 ); +constexpr auto BIT_ONLYRETURNTOBANK = UI32( 125 ); // New uox3.ini format lookup @@ -953,6 +964,16 @@ auto CServerData::ResetDefaults() -> void AmbientFootsteps( false ); ServerCommandPrefix( '\'' ); + // Player vendor upkeep settings + VendorChargesEnabled( true ); + VendorBaseCharge( 60 ); + VendorChargeHours( 24 ); + VendorUseItemFeesEnabled( true ); + VendorItemFeeDivisor( 500 ); + VendorItemFeeAmount( 3 ); + OnlyReturnToBank( true ); + VendorMaxFunds( 1000000 ); + // SPEEDUP CheckSpawnRegionSpeed( 30 ); CheckItemsSpeed( 1.5 ); @@ -5475,6 +5496,118 @@ auto CServerData::KickOnAssistantSilence( bool nVal ) -> void boolVals.set( BIT_KICKONASSISTANTSILENCE, nVal ); } +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorChargesEnabled() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets whether player vendors charge upkeep fees +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorChargesEnabled() const -> bool +{ + return boolVals.test( BIT_VENDORCHARGESENABLED ); +} +auto CServerData::VendorChargesEnabled( bool newVal ) -> void +{ + boolVals.set( BIT_VENDORCHARGESENABLED, newVal ); +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorBaseCharge() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets the flat gold fee charged to player vendors each upkeep period +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorBaseCharge() const -> SI16 +{ + return vendorBaseCharge; +} +auto CServerData::VendorBaseCharge( SI16 value ) -> void +{ + vendorBaseCharge = value; +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorChargeHours() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets how often player vendors are charged upkeep fees, in real hours +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorChargeHours() const -> SI16 +{ + return vendorChargeHours; +} +auto CServerData::VendorChargeHours( SI16 value ) -> void +{ + vendorChargeHours = value; +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorUseItemFeesEnabled() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets whether listed item values add extra player vendor upkeep fees +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorUseItemFeesEnabled() const -> bool +{ + return boolVals.test( BIT_VENDORUSEITEMFEESENABLED ); +} +auto CServerData::VendorUseItemFeesEnabled( bool newVal ) -> void +{ + boolVals.set( BIT_VENDORUSEITEMFEESENABLED, newVal ); +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorItemFeeDivisor() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets the listed item value divisor used for player vendor item fees +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorItemFeeDivisor() const -> SI16 +{ + return vendorItemFeeDivisor; +} +auto CServerData::VendorItemFeeDivisor( SI16 value ) -> void +{ + vendorItemFeeDivisor = value; +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorItemFeeAmount() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets the gold fee added per player vendor item fee divisor step +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorItemFeeAmount() const -> SI16 +{ + return vendorItemFeeAmount; +} +auto CServerData::VendorItemFeeAmount( SI16 value ) -> void +{ + vendorItemFeeAmount = value; +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::OnlyReturnToBank() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets returned vendor belongings skip player backpack entirely +//o------------------------------------------------------------------------------------------------o +auto CServerData::OnlyReturnToBank() const -> bool +{ + return boolVals.test( BIT_ONLYRETURNTOBANK ); +} +auto CServerData::OnlyReturnToBank( bool newVal ) -> void +{ + boolVals.set( BIT_ONLYRETURNTOBANK, newVal ); +} + +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::VendorMaxFunds() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets the Max amount of money the vendor can hold in the bank. +//o------------------------------------------------------------------------------------------------o +auto CServerData::VendorMaxFunds() const -> UI32 +{ + return vendorMaxFunds; +} +auto CServerData::VendorMaxFunds( UI32 value ) -> void +{ + vendorMaxFunds = value; +} + //o------------------------------------------------------------------------------------------------o //| Function - CServerData::SaveIni() //| Date - 02/21/2002 @@ -5842,6 +5975,17 @@ auto CServerData::SaveIni( const std::string &filename ) -> bool ofsOutput << "PETBONDINGENABLED=" << ( PetBondingEnabled() ? 1 : 0 ) << '\n'; ofsOutput << "}" << '\n'; + ofsOutput << '\n' << "[playervendor]" << '\n' << "{" << '\n'; + ofsOutput << "VENDORCHARGESENABLED=" << ( VendorChargesEnabled() ? 1 : 0 ) << '\n'; + ofsOutput << "VENDORBASECHARGE=" << VendorBaseCharge() << '\n'; + ofsOutput << "VENDORCHARGEHOURS=" << VendorChargeHours() << '\n'; + ofsOutput << "VENDORUSEITEMFEESENABLED=" << ( VendorUseItemFeesEnabled() ? 1 : 0 ) << '\n'; + ofsOutput << "VENDORITEMFEEDIVISOR=" << VendorItemFeeDivisor() << '\n'; + ofsOutput << "VENDORITEMFEEAMOUNT=" << VendorItemFeeAmount() << '\n'; + ofsOutput << "ONLYRETURNTOBANK=" << ( OnlyReturnToBank() ? 1 : 0 ) << '\n'; + ofsOutput << "VENDORMAXFUNDS=" << VendorMaxFunds() << '\n'; + ofsOutput << "}" << '\n'; + ofsOutput << '\n' << "[speedup]" << '\n' << "{" << '\n'; ofsOutput << "CHECKITEMS=" << CheckItemsSpeed() << '\n'; ofsOutput << "CHECKBOATS=" << CheckBoatSpeed() << '\n'; @@ -7587,6 +7731,31 @@ auto CServerData::HandleLine( const std::string& tag, const std::string& value ) break; case 407: // LOGINQUESTENABLED LoginQuestEnabled( ( static_cast( std::stoul( value, nullptr, 0 ) ) >= 1 ? true : false ) ); + break; + case 408: // VENDORCHARGESENABLED + VendorChargesEnabled( ( static_cast( std::stoul( value, nullptr, 0 ) ) >= 1 ? true : false ) ); + break; + case 409: // VENDORBASECHARGE + VendorBaseCharge( static_cast( std::stoi( value ))); + break; + case 410: // VENDORCHARGEHOURS + VendorChargeHours( static_cast( std::stoi( value ))); + break; + case 411: // VENDORUSEITEMFEESENABLED + VendorUseItemFeesEnabled( ( static_cast( std::stoul( value, nullptr, 0 ) ) >= 1 ? true : false ) ); + break; + case 412: // VENDORITEMFEEDIVISOR + VendorItemFeeDivisor( static_cast( std::stoi( value ))); + break; + case 413: // VENDORITEMFEEAMOUNT + VendorItemFeeAmount( static_cast( std::stoi( value ))); + break; + case 414: // ONLYRETURNTOBANK + OnlyReturnToBank( ( static_cast( std::stoul( value, nullptr, 0 ) ) >= 1 ? true : false ) ); + break; + case 415: // VENDORMAXFUNDS + VendorMaxFunds( static_cast( std::stoul( value, nullptr, 0 ))); + break; default: rValue = false; break; diff --git a/source/cServerData.h b/source/cServerData.h index 9bed4047b..030b28036 100644 --- a/source/cServerData.h +++ b/source/cServerData.h @@ -212,7 +212,7 @@ class CServerData // Once over 62, bitsets are costly. std::vector has a special exception in the c++ specificaiton, to minimize wasted space for bools // These should be updated - std::bitset<123> boolVals; // Many values stored this way, rather than using bools. + std::bitset<126> boolVals; // Many values stored this way, rather than using bools. std::bitset<64> spawnRegionsFacets; // Used to determine which facets to enable spawn regions for, set in UOX>INI std::bitset<64> moongateFacets; // Used to determine which facets to enable moongates for, set in UOX>INI @@ -337,6 +337,15 @@ class CServerData UI16 petLoyaltyGainOnSuccess; // The default amount of pet loyalty gained on successful use of a pet command UI16 petLoyaltyLossOnFailure; // The default amount of pet loyalty lost on a failed attempt to use a pet command + bool vendorChargesEnabled; // Master toggle for player vendor upkeep charges + SI16 vendorBaseCharge; // Flat fee per vendor upkeep charge period + SI16 vendorChargeHours; // Number of real hours between vendor upkeep charges + bool vendorUseItemFeesEnabled; // Add item-based upkeep fees from listed item prices + SI16 vendorItemFeeDivisor; // Listed item value divisor used for vendor item fees + SI16 vendorItemFeeAmount; // Fee added per divisor step + bool onlyReturnToBank; // returned vendor belongings skip player backpack entirely + UI32 vendorMaxFunds; // Max amount of money the vendor can hold in the bank. + // SpeedUp R64 npcWalkingSpeed; // Speed at which walking NPCs move R64 npcRunningSpeed; // Speed at which running NPCs move @@ -1387,6 +1396,30 @@ class CServerData auto APSDelayMaxCap() const -> UI16; auto APSDelayMaxCap( UI16 newVal ) -> void; + auto VendorChargesEnabled( bool value ) -> void; + auto VendorChargesEnabled() const -> bool; + + auto VendorBaseCharge( SI16 value ) -> void; + SI16 VendorBaseCharge() const; + + auto VendorChargeHours( SI16 value ) -> void; + SI16 VendorChargeHours() const; + + auto VendorUseItemFeesEnabled( bool value ) -> void; + auto VendorUseItemFeesEnabled() const -> bool; + + auto VendorItemFeeDivisor( SI16 value ) -> void; + SI16 VendorItemFeeDivisor() const; + + auto VendorItemFeeAmount( SI16 value ) -> void; + SI16 VendorItemFeeAmount() const; + + auto OnlyReturnToBank( bool value ) -> void; + auto OnlyReturnToBank() const -> bool; + + auto VendorMaxFunds( UI32 value ) -> void; + UI32 VendorMaxFunds() const; + SI16 ServerTimeDay() const; UI08 ServerTimeHours() const; UI08 ServerTimeMinutes() const;