UNPKG

alclient

Version:

A node client for interacting with Adventure Land - The Code MMORPG. This package extends the functionality of 'alclient' by managing a mongo database.

381 lines 16.2 kB
import { Constants } from "./Constants.js"; import { PingCompensatedCharacter } from "./PingCompensatedCharacter.js"; import { Tools } from "./Tools.js"; export class Merchant extends PingCompensatedCharacter { ctype = "merchant"; /** * Fish for items. * * @return {*} {Promise<void>} * @memberof Merchant */ async fish() { if (!this.ready) throw new Error("We aren't ready yet [fish]."); if (this.c.fishing) return; // We're already fishing // TODO: Add area check if we can fish here // Start fishing const started = this.getResponsePromise("fishing"); this.socket.emit("skill", { name: "fishing" }); return started.then(() => { return new Promise((resolve, reject) => { const cleanup = () => { this.socket.off("ui", noneCheck); this.socket.off("game_log", logCheck); this.socket.off("skill_timeout", cooldownCheck); clearTimeout(timeout); }; const noneCheck = (data) => { if (data.type == "fishing_none") { cleanup(); resolve("We didn't fish anything."); } }; let log; const logCheck = (data) => { if (typeof data !== "object") return; const fishRegex = /^Fished an* .+/.exec(data.message); if (fishRegex) log = fishRegex[0]; }; const cooldownCheck = (data) => { if (data.name == "fishing") { cleanup(); resolve(log); } }; const timeout = setTimeout(() => { cleanup(); reject(new Error("fish timeout (20000ms)")); }, 20000); this.socket.on("ui", noneCheck); this.socket.on("game_log", logCheck); this.socket.on("skill_timeout", cooldownCheck); }); }); } // TODO: Add promises async joinGiveaway(slot, id, rid) { if (!this.ready) throw new Error("We aren't ready yet [joinGiveaway]."); const merchant = this.players.get(id); if (!merchant || Tools.squaredDistance(this, merchant) > Constants.NPC_INTERACTION_DISTANCE_SQUARED) throw new Error(`${id} is too far away.`); if (!merchant.slots[slot]?.giveaway) throw new Error(`${id}'s slot ${slot} is not a giveaway.`); if (merchant.slots[slot]?.list.includes(this.id)) return; // We've already joined it // const joined = new Promise<void>((resolve, reject) => { // // TODO // }) this.socket.emit("join_giveaway", { id: id, rid: rid, slot: slot }); // return joined } /** * Lists an item for sale on your merchant stand * * @param {number} itemPos the position of the item in your inventory * @param {number} price the price to sell the item * @param {TradeSlotType} [tradeSlot] the trade slot to list the item in * @param {number} [quantity=1] the number of items to sell at this price * @return {*} {Promise<unknown>} * @memberof Merchant */ async listForSale(itemPos, price, tradeSlot, quantity = 1) { if (!this.ready) throw new Error("We aren't ready yet [listForSale]."); const itemInfo = this.items[itemPos]; if (!itemInfo) throw new Error(`We do not have an item in slot ${itemPos}`); if (price <= 0) throw new Error("The lowest you can set the price is 1."); if (quantity <= 0) throw new Error("The lowest you can set the quantity to is 1."); const gInfo = this.G.items[itemInfo.name]; if (!tradeSlot && itemInfo.q) { // Look for an existing item to stack for sale for (const slotName in this.slots) { if (!slotName.startsWith("trade")) continue; // Not a trade slot const slotInfo = this.slots[slotName]; if (!slotInfo) continue; // Nothing in this slot if (slotInfo.name !== itemInfo.name) continue; // Check if it's the same item if (slotInfo.p !== itemInfo.p) continue; if (slotInfo.data !== itemInfo.data) continue; if (quantity + slotInfo.q > gInfo.s) continue; // Check if it's stackable if (price < slotInfo.price) continue; // We're listing it for less, don't list them all at this price. tradeSlot = slotName; break; } } if (!tradeSlot) { // Look for an empty trade slot to list this item in for (const slotName in this.slots) { if (!slotName.startsWith("trade")) continue; // Not a trade slot const slotInfo = this.slots[slotName]; if (slotInfo) continue; tradeSlot = slotName; break; } if (!tradeSlot) throw new Error("We don't have an empty trade slot to list the item for sale."); } const slotInfo = this.slots[tradeSlot]; if (slotInfo) { if (itemInfo.name == slotInfo.name && // Same item itemInfo.p == slotInfo.p && itemInfo.data == slotInfo.data && price >= slotInfo.price && // Same, or higher price gInfo.s && quantity + slotInfo.q <= gInfo.s && // Stackable this.esize > 0 // NOTE: Unequipping trade items only works if we have an empty slot, even if we can stack the items ) { if (itemPos !== 0) { // Swap items so when it gets stacked, it gets stacked in the correct position await this.swapItems(0, itemPos); } // Unequip, so we can combine the two slots await this.unequip(tradeSlot); quantity += slotInfo.q; if (itemPos !== 0) { // Swap back await this.swapItems(0, itemPos); } } else { throw new Error(`We are already trading something in ${tradeSlot}.`); } } const listed = new Promise((resolve, reject) => { const failCheck1 = (data) => { if (typeof data == "string") { if (data == "slot_occupied") { this.socket.off("game_response", failCheck1); this.socket.off("disappearing_text", failCheck2); this.socket.off("player", successCheck); reject(new Error(`We are already listing something in ${tradeSlot}.`)); } } }; const failCheck2 = (data) => { if (data.message == "CAN'T EQUIP" && data.id == this.id) { this.socket.off("game_response", failCheck1); this.socket.off("disappearing_text", failCheck2); this.socket.off("player", successCheck); reject(new Error(`We failed listing the item in ${tradeSlot}.`)); } }; const successCheck = (data) => { const newTradeSlot = data.slots[tradeSlot]; if (newTradeSlot && newTradeSlot.name == itemInfo.name && newTradeSlot.q == quantity) { this.socket.off("game_response", failCheck1); this.socket.off("disappearing_text", failCheck2); this.socket.off("player", successCheck); resolve(); } }; setTimeout(() => { this.socket.off("game_response", failCheck1); this.socket.off("disappearing_text", failCheck2); this.socket.off("player", successCheck); reject(new Error("listForSale timeout (1000ms)")); }, 1000); this.socket.on("game_response", failCheck1); this.socket.on("disappearing_text", failCheck2); this.socket.on("player", successCheck); }); this.socket.emit("equip", { num: itemPos, price: price, q: quantity, slot: tradeSlot, }); return listed; } /** * NOTE: Untested * * Adds an item that you want to purchase to your trade listing * * To remove a listing, call unequip(tradeSlot) * * @param itemName The item you want to buy * @param price The price you'll pay for it * @param tradeSlot The trade slot you want to put the request in * @param quantity How many of the item you want to buy * @param level For equipment, the level of the item you wish to buy */ async listForPurchase(itemName, price, tradeSlot, quantity = 1, level) { if (!this.ready) throw new Error("We aren't ready yet [listForPurchase]."); if (price <= 0) throw new Error("The lowest you can set the price is 1."); if (quantity <= 0) throw new Error("The lowest you can set the quantity to is 1."); if (!tradeSlot) { for (const slotName in this.slots) { if (!slotName.startsWith("trade")) continue; // Not a trade slot const slotInfo = this.slots[slotName]; if (slotInfo) continue; tradeSlot = slotName; break; } if (!tradeSlot) throw new Error("We don't have any empty trade slot to wishlist the item."); } else { if (this.slots[tradeSlot]) throw new Error(`We already have something listed in '${tradeSlot}'.`); if (this.slots[tradeSlot] === undefined) throw new Error(`We don't have a trade slot '${tradeSlot}'.`); } const wishListed = new Promise((resolve, reject) => { const successCheck = (data) => { const newTradeSlot = data.slots[tradeSlot]; if (!newTradeSlot) return; // No data (yet?) if (!newTradeSlot.b) return; if (newTradeSlot.name !== itemName) return; if (newTradeSlot.q !== quantity) return; if (newTradeSlot.price !== price) return; this.socket.off("player", successCheck); this.socket.off("game_response", failCheck); resolve(); }; const failCheck = (data) => { if (typeof data == "string") { if (data == "slot_occupied") { this.socket.off("player", successCheck); this.socket.off("game_response", failCheck); reject(new Error(`We already have something listed in '${tradeSlot}'.`)); } } }; setTimeout(() => { this.socket.off("player", successCheck); this.socket.off("game_response", failCheck); reject(new Error("listForPurchase timeout (1000ms)")); }, 1000); this.socket.on("player", successCheck); this.socket.on("game_response", failCheck); }); this.socket.emit("trade_wishlist", { level: level, name: itemName, price: price, q: quantity, slot: tradeSlot, }); return wishListed; } async merchantCourage() { if (!this.ready) throw new Error("We aren't ready yet [merchantCourage]."); const response = this.getResponsePromise("mcourage"); this.socket.emit("skill", { name: "mcourage" }); return response; } async mine() { if (!this.ready) throw new Error("We aren't ready yet [mine]."); if (this.c.mining) return; // We're already mining // TODO: Add area check if we can mine here // Start mining const started = this.getResponsePromise("mining"); this.socket.emit("skill", { name: "mining" }); return started.then(() => { return new Promise((resolve, reject) => { const cleanup = () => { this.socket.off("ui", noneCheck); this.socket.off("game_log", logCheck); this.socket.off("skill_timeout", cooldownCheck); clearTimeout(timeout); }; const noneCheck = (data) => { if (data.type == "mining_none") { cleanup(); resolve("We didn't mine anything."); } }; let log; const logCheck = (data) => { if (typeof data !== "object") return; const mineRegex = /^Mined an* .+/.exec(data.message); if (mineRegex) log = mineRegex[0]; }; const cooldownCheck = (data) => { if (data.name == "mining") { cleanup(); resolve(log); } }; const timeout = setTimeout(() => { cleanup(); reject(new Error("mine timeout (20000ms)")); }, 20000); this.socket.on("ui", noneCheck); this.socket.on("game_log", logCheck); this.socket.on("skill_timeout", cooldownCheck); }); }); } async mluck(target) { if (!this.ready) throw new Error("We aren't ready yet [mluck]."); if (target !== this.id) { const player = this.players.get(target); if (!player) throw new Error(`Could not find ${target} to mluck.`); if (player.npc) throw new Error(`${target} is an NPC. You can't mluck NPCs.`); if (player.s.mluck && player.s.mluck.strong && // They have a strong mluck ((player.owner && player.owner !== this.owner) || // If they are public, check if they are from different owners player.s.mluck.f !== this.id)) { // Else, rely on the character id throw new Error(`${target} has a strong mluck from ${player.s.mluck.f}.`); } } const response = this.getResponsePromise("mluck"); this.socket.emit("skill", { id: target, name: "mluck" }); return response; } async massProduction() { if (!this.ready) throw new Error("We aren't ready yet [massProduction]."); if (this.s.massproduction) return; // We already have it active const response = this.getResponsePromise("massproduction"); this.socket.emit("skill", { name: "massproduction" }); return response; } massProductionPP() { if (!this.ready) throw new Error("We aren't ready yet [massProductionPP]."); if (this.s.massproductionpp) return; // We already have it active const response = this.getResponsePromise("massproductionpp"); this.socket.emit("skill", { name: "massproductionpp" }); return response; } } //# sourceMappingURL=Merchant.js.map