Merchant Example Script - lordachoo/adventureland GitHub Wiki

Merchant Example Code Description

  • Stuff

Goblin Guy (Ron)

  • Location: Wizards Crib -- Left of Main map , Main Area
  • Donate: 1M Gold == 3.2M Exp (3.2 gold/exp)

Code

Interactive Merchant Mode

if (character.map == "main") { smart_move("mansion") } else { smart_move("main") } 

function destroy(inventoryIndex, itemQuantity=1) {
  // take an inventory index and optional quantity and
  // emit a socket event to destroy the item(s)

  // invalid inventory index
  // Note: probably not needed if the server does the check anyway
  // though there may be value in reducing latency overhead by checking
  // here on the client
  if (inventoryIndex < 0 || inventoryIndex > 41) return;

  // !NaN => true; valid number required
  if (!parseInt(itemQuantity)) return;

  // quantity must be between 1 and 9999
  if (itemQuantity < 1 || itemQuantity > 9999) return;

  // define what we're destroy & how many
  let destroyPayload = {
    num: inventoryIndex,
    q: itemQuantity,
    statue: true,
  }

  // emit the destruction of the item
  parent.socket.emit("destroy", destroyPayload);
}
//var destroyItemID = locate_item("wbreeches");
//destroy(destroyItemID)
//var potion = locate_item("mpot0");
//send_item("LordAsneeze", potion, 9999)

Go To "X" Camp

// spooky forest snakes
smart_move({ x: 267 , y: -702, map: 'halloween'})

// Ponty/Mainmap
smart_move({ x: -3 , y: -3, map: 'main'})

if (character.map == "main") { smart_move("mansion") } else { smart_move("main") } 

Get Items from Bots (Comm)

// move all items to Merch
// Purposely NOT transfering up to slot 41 (Trackatrix slot)
for (let i = 0; i < 40; i++) { send_item("LordAjew", i, 9999); }
// send gold
send_gold("LordAjew", 11800000)
// Move item on bots from first to last
swap(0,41)

var potion = locate_item("mpot0");
send_item("LordAsneeze", potion, 9999)

Merchant booth auto open/close

  • You get merchant exp for having booth open periodically so this lets you leave it open but still run around.
  • Must be in an interval check so it keeps checking / triggering
// Every second example
setInterval(function(){
    if (character.ctype === "merchant" && is_moving(character) && character.stand) close_stand();
    if (character.ctype === "merchant" && !is_moving(character) && !character.stand) open_stand();
},1000);

Lazy mine (once)

  • Will move to tunnel and mine until it mines, cooldown after you get a fragment is 7440 seconds (2 hours 4 minutes)
// Go to the wall
await smart_move({ x: -264 , y: -195, map: 'tunnel'})
setInterval(function(){
	use_hp_or_mp();
    if (character.ctype === "merchant" && !is_moving(character) && can_use("mining") && character.mp > 119) { 
		set_message("mining"); 
		use_skill("mining");
	}
},1000);

Lazy upgrade

// Item in 0
// Upgrade scroll in 1
for (let i = 0; i < 7; i++) {
	if (can_use("massproduction")) use_skill("massproduction")
	await upgrade(0,1);
}

Lazy Exchange

// Item in 0
for (let i = 0; i < 999; i++) {
	await exchange(0);
}

Auto Exchange

var eItem = false; //Enable exchanging of items = true, Disable exchanging of items = false
var whitelist = []; //whitelist is for the exchanging of items

setInterval(function() {

  //exchanges items in whitelist
  if (eItem) {
    exchangeItem()
  }

}, 1000 / 4); //Loop every 1/4 seconds.

function exchangeItem() {
  for (let i = 0; i < character.items.length; i++) {
    let c = character.items[i];
    if (c) {
      if (c && whitelist.includes(c.name)) {

        exchange(i)
        parent.e_item = i;
      }
    }
  }
}

Auto Sell

var sItem = false; //Enable selling of items = true, Disable selling of items = false
var whitelist = []; //whitelist is for the selling of items

setInterval(function() {

  //sells items in whitelist
  if (sItem) {
    sellItem()
  }

}, 1000 / 4); //Loop every 1/4 seconds.

function sellItem() {
  for (let i = 0; i < character.items.length; i++) {
    let c = character.items[i];
    if (c) {
      if (c && whitelist.includes(c.name)) {

        sell(i);
      }
    }
  }
}

Auto Compound

var whitelist = ['wbook0', 'intamulet', 'stramulet', 'dexamulet', 'intearring', 'strearring', 'dexearring', 'hpbelt', 'hpamulet', 'ringsj', 'amuletofm', 'orbofstr', 'orbofint', 'orbofres', 'orbofhp'];
var use_better_scrolls = false; //240,000 Gold Scroll = true [only will use for +2 and higher], 6,400 Gold Scroll = false [will only use base scroll no matter what]
var maxLevel = 3;
//compound settings

if (enableCompound) {
	setInterval(async function() {

	  //Compound Items

		await compound_items();

	}, COMPOUND_TIMEOUT); 
}

async function compound_items() {
  let to_compound = character.items.reduce((collection, item, index) => {
    if (item && item.level < maxLevel && whitelist.includes(item.name)) {
      let key = item.name + item.level;
      !collection.has(key) ? collection.set(key, [item.level, index]) : collection.get(key).push(index);
    }
    return collection;
  }, new Map());

  for (var c of to_compound.values()) {
    let scroll_name = use_better_scrolls && c[0] > 1 ? 'cscroll1' : 'cscroll0';

    for (let i = 1; i + 2 < c.length; i += 3) {
      let [scroll, _] = find_item(i => i.name == scroll_name);
      if (scroll == -1) {
        parent.buy(scroll_name);
        return;
      }
      parent.socket.emit('compound', {
        items: [c[i], c[i + 1], c[i + 2]],
        scroll_num: scroll,
        offering_num: null,
        clevel: c[0]
      });
    }
  }
}

function find_item(filter) {
  for (let i = 0; i < character.items.length; i++) {
    let item = character.items[i];

    if (item && filter(item))
      return [i, character.items[i]];
  }

  return [-1, null];
}

Auto Upgrades

var emaxlevel = 7; //Max level it will stop upgrading items at if enabled
var whitelist = ['wgloves', 'wbreeches',]; 

if(enableUpgrades) {
	setInterval(async function() {
		await upgrade(emaxlevel);
	}, UPGRADE_TIMEOUT);
}

async function upgrade(level) {
	set_message("UPGRADING");
  for (let i = 0; i < character.items.length; i++) {
    let c = character.items[i];
    if (c && whitelist.includes(c.name) && c.level < level) {
      let grades = get_grade(c);
      let scrollname;
      if (c.level < grades[0])
        scrollname = 'scroll0';
      else if (c.level < grades[1])
        scrollname = 'scroll1';
      else
        scrollname = 'scroll2';

      let [scroll_slot, scroll] = find_item(i => i.name == scrollname);
      if (!scroll) {
        parent.buy(scrollname);
        return;
      }

      parent.socket.emit('upgrade', {
        item_num: i,
        scroll_num: scroll_slot,
        offering_num: null,
        clevel: c.level
      });
      return;
    }
  }
}

function get_grade(item) {
  return parent.G.items[item.name].grades;
}

// Returns the item slot and the item given the slot to start from and a filter.
function find_item(filter) {
  for (let i = 0; i < character.items.length; i++) {
    let item = character.items[i];

    if (item && filter(item))
      return [i, character.items[i]];
  }

  return [-1, null];
}

Mining / Fishing (Autonomous)

if(autoMineFish) {
	var hasRod = locate_item("rod");
	var hasPick = locate_item("pickaxe");
	var mainhand = character.slots.mainhand?.name;
	
	// Fishing/Mining Check
	// If you can mine, go mine
	// If you can fish, go fish
	// If you've done both, park ass in main for ponty/upgrade/combines
	setInterval(async function(){
		if(can_use("mining") && hasPick != -1 || mainhand === "pickaxe") {
			set_message("mining");
			custom_log("mining");
			do_action("mining");
		}
		if(can_use("fishing") && !can_use("mining") && hasRod != -1 || mainhand === "rod") {
			set_message("Fishing");
			custom_log("Fishing");
			do_action("fishing");
		}
		if(!can_use("mining") && !can_use("fishing")) {
			set_message("Fished/Mined");
			custom_log("Fished/Mined");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
		if(!can_use("fishing") && can_use("mining") && hasPick === -1) {
			set_message("Fished, No Pickaxe");
			custom_log("Fished, No Pickaxe");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
		if(!can_use("mining") && can_use("fishing") && hasRod === -1) {
			set_message("Mined, No Rod");
			custom_log("Mined, No Rod");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
		// Needs to check if you have either tool EQUIPPED OR in inventory
		if(can_use("mining") && can_use("fishing") && hasRod === -1 && hasPick === -1 && mainhand != "pickaxe" && mainhand != "rod") {
			set_message("No Tools!");	
			custom_log("No Tools!");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
	},1000 * 60);
}

/*
* DO_ACTION // FISH / MINING
*/
function do_action(action) {
        if (IGNORE_ACTION) return

        // dont do if there's something else going on
        //if (!action || this.current_action || smart.moving)
	if (!action || smart.moving)
            return;

        // don't do anything while it's on cooldown
        if (is_on_cooldown(action)) {
            return;
        }

        // what to equip per skill
        let item_map = {
            fishing: "rod",
            mining: "pickaxe"
        }

        // where to go
        let location_map = {
            fishing: {map: 'main', x: -1368, y: -216},
            mining: {map: 'tunnel', x: -280, y: -26}
        }
        let location = location_map[action]

        set_current_action(action);
        smart_move(location)
            .then(
            (success) => {
                custom_log("got to destination")
                // turn on current action
                set_current_action(action);

                let itemName = item_map[action]
                let itemIndex = locate_item(itemName)
                if (!character.slots.mainhand || !(character.slots.mainhand.name == itemName));
                    equip(itemIndex)
					custom_log("Equip Item: "+itemIndex);

                doAction(action);
            },
            (failure) => {
                custom_log(`Couldn't go ${action}`);
            }
        )
}

function set_current_action(action) {
	var currentAction = action;
	set_message("Set Action"+action);
}

function clear_current_action(action) {
	set_message("Clear Action"+action);
	var currentAction = "none";
}

const doAction = (action) => {
	set_message("Action:"+ action);
	  if(character.mp < 119) {
		  //close.stand();
		  use_hp_or_mp();
	  }
      if (is_on_cooldown(action)) {
        clear_current_action(action);
        unequip("mainhand");
        if (quantity("broom")) {
          equip(locate_item("broom"));
        }
        return;
      }

      if (!character.c[action]) {
        use_skill(action);
      }

      // item broke
      if (!character.slots.mainhand) {
        custom_log("Tool broke: " + itemName);
        clear_current_action(action);
      }
      setTimeout(() => doAction(action), parent.character.c[action]?.ms ?? 1000);
}

/////////// END do_action / Mining/Fishing

Mass Auto List Items (Non Stackable Stuff)

let sellTimer = null;

function sell_item(itemName, itemPrice, quantity=1) {

    quantity = quantity ?? 1;

    // locate item index using name
    let itemIndex = locate_item(itemName);
    
    log(`index: ${itemIndex}`);

    // item doesn't exist in inventory if less than 0
    if (itemIndex < 0) return false;

    // iterate over character slots, look for trade slots
    Object.keys(character.slots).filter(key => key.includes("trade")).forEach( (key, tradeIndex) => {
        // trade slots start at 1, e.g. trade1
        tradeIndex += 1;

        log(`Key: ${key} and tradeIndex${tradeIndex}`);

        // check if there's a slotted item in there
        let slottedItem = character.slots[key];
        log(`Slotted item: ${slottedItem?.name}`)

        // skip if so
        if (slottedItem?.name) return;

        log(`Putting ${quantity}x item ${itemName} in inventory's spot ${itemIndex} into the trade spot trade${tradeIndex} at the price ${itemPrice}`)

        // getting here we have an empty space
        trade(itemIndex, tradeIndex, itemPrice, quantity)
    })
}

function sellItem(itemName, price) {
    
    // check to see if there's an existing order on the stand
    let buyOrderUp = false;
    Object.keys(character.slots).filter(key => key.includes("trade")).forEach( key => {
        if (key === itemName) {
            buyOrderUp = true;
        }
    });
    
    if (!quantity(itemName)) {
        log(`No more ${itemName} in inventory`);
        // dont set more timeouts
        return;
    }
        
    // if there's a buy order, check again in a little bit
    if (buyOrderUp) {
        // set timer and exit the func
        sellTimer = setTimeout(() => {
            sellItem(itemName, price)
        }, 1000);
        return;
    }
        
    // the item should exist
    result = sell_item(itemName, price)
    
    if (result === false) {
        return
    }
    // start the timer
    sellTimer = setTimeout(() => sellItem(itemName, price), 1000);
    
}

sellItem("lostearring", 1250000)

Example Compounded/Combined Merchant Script

  • Pony Buy
  • Walk to Gobbo (although he doesn't unlock his list automatically or use the thing to keep it unlocked after disconn)
  • Auto Upgrade (with options)
  • Auto Compound (with options)
var attack_mode=false
var ENABLE_LOGS=true
var ENABLE_PONTY=true
var PONTY_TIMEOUT=60000 * 2
var enableGoblinCheck=true
var GOBLIN_TIMEOUT=60000 * 2
var DESTROY_13_ENABLED=false
var enableCompound=true
var COMPOUND_TIMEOUT=6000
var enableUpgrades=true
var UPGRADE_TIMEOUT=5000
var ENABLE_BUY_TOOLS=true
var IGNORE_ACTION=false
var autoMineFish=false
var debug=true
var updateBankAPI=true
var sItem = true; 
//var currentAction="";

/*
* SELL LIST
*/
var sellWhitelist = ['wbreeches',]; //whitelist is for the selling of items
////////

if(updateBankAPI) {
	setInterval(async function() {
	//set_message("Bank API Update");
	custom_log("Bank API Update");
	await smart_move("bank");
	await UpdateEarthBank();	
	await smart_move({ x: -195 , y: -111, map: 'main'});
	},500000);
}

// Whip Merchant Booth Out
	setInterval(async function(){
		var mainhand = character.slots.mainhand?.name;
		use_hp_or_mp();
		set_message("Booth Check");
		var mainhand = character.slots.mainhand?.name;
		// If Mainhand is not broom already, or if we're in tunnel, or mining/fishing
		//if(mainhand != "broom" || character.map != "tunnel" || currentAction != "fishing" || currentAction != "mining") {
			//custom_log(currentAction);
		//var broom = locate_item("broom");
		//equip(broom);
		//}
		if (character.ctype === "merchant" && is_moving(character) && character.stand) close_stand();
		if (character.ctype === "merchant" && !is_moving(character) && !character.stand) open_stand();
		
		// Faster mining
		if(character.map === "tunnel" && mainhand === "pickaxe" && can_use("mining")) {
		set_message("Fast mining");
		await do_action("mining");
		}
	},1000);

if(autoMineFish) {
	// Fishing/Mining Check
	// If you can mine, go mine
	// If you can fish, go fish
	// If you've done both, park ass in main for ponty/upgrade/combines
	setInterval(async function(){
	var hasRod = locate_item("rod");
	var hasPick = locate_item("pickaxe");
	var mainhand = character.slots.mainhand?.name;
		if(can_use("mining") && hasPick != -1 || mainhand === "pickaxe") {
			set_message("mining");
			custom_log("mining");
			await do_action("mining");
		}
		if(can_use("fishing") && !can_use("mining") && hasRod != -1) {
			set_message("Fishing");
			custom_log("Fishing");
			await do_action("fishing");
		}
		if(mainhand === "rod" && can_use("fishing") && !can_use("mining")) {
			set_message("Fishing");
			custom_log("Fishing");
			await do_action("fishing");		
		}
		if(!can_use("mining") && !can_use("fishing")) {
			set_message("Fished/Mined");
			custom_log("Fished/Mined");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
		if(!can_use("fishing") && can_use("mining") && hasPick === -1) {
			set_message("Fished, No Pickaxe");
			custom_log("Fished, No Pickaxe");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
		//if(!can_use("mining") && can_use("fishing") && hasRod === -1) {
		//	set_message("Mined, No Rod");
		//	custom_log("Mined, No Rod");
		//	await smart_move({ x: -3 , y: -3, map: 'main'})
		//}
		// Needs to check if you have either tool EQUIPPED OR in inventory
		if(can_use("mining") && can_use("fishing") && hasRod === -1 && hasPick === -1 && mainhand != "pickaxe" && mainhand != "rod") {
			set_message("No Tools!");	
			custom_log("No Tools!");
			await smart_move({ x: -3 , y: -3, map: 'main'})
		}
		set_message("Tool Eq Check");
		//var mainhand = character.slots.mainhand?.name;
		if(mainhand === "rod" || mainhand === "pickaxe") {
			custom_log("Unequiping rod/pickaxe");	
			unequip("mainhand");
		}
	},1000 * 60);
}

/*
* DO_ACTION // FISH / MINING
*/
function do_action(action) {
        if (IGNORE_ACTION) return

        // dont do if there's something else going on
        //if (!action || this.current_action || smart.moving)
	if (!action || smart.moving)
            return;

        // don't do anything while it's on cooldown
        if (is_on_cooldown(action)) {
            return;
        }

        // what to equip per skill
        let item_map = {
            fishing: "rod",
            mining: "pickaxe"
        }

        // where to go
        let location_map = {
            fishing: {map: 'main', x: -1368, y: -216},
            mining: {map: 'tunnel', x: -280, y: -26}
        }
        let location = location_map[action]

        set_current_action(action);
        smart_move(location)
            .then(
            (success) => {
                custom_log("got to destination")
                // turn on current action
                set_current_action(action);

                let itemName = item_map[action]
                let itemIndex = locate_item(itemName)
                if (!character.slots.mainhand || !(character.slots.mainhand.name == itemName));
                    equip(itemIndex)
					custom_log("Equip Item: "+itemIndex);

                doAction(action);
            },
            (failure) => {
                custom_log(`Couldn't go ${action}`);
            }
        )
}

function set_current_action(action) {
	var currentAction = action;
	set_message("Set Action"+action);
}

function clear_current_action(action) {
	set_message("Clear Action"+action);
	var currentAction = "none";
}

const doAction = (action) => {
	set_message("Action:"+ action);
	  if(character.mp < 119) {
		  //close.stand();
		  use_hp_or_mp();
	  }
      if (is_on_cooldown(action)) {
        clear_current_action(action);
        unequip("mainhand");
        if (quantity("broom")) {
          equip(locate_item("broom"));
        }
        return;
      }

      if (!character.c[action]) {
        use_skill(action);
      }

      // item broke
      if (!character.slots.mainhand) {
        custom_log("Tool broke: " + itemName);
        clear_current_action(action);
      }
      setTimeout(() => doAction(action), parent.character.c[action]?.ms ?? 1000);
}

/////////// END do_action / Mining/Fishing

/// BUY LISTS
let PONTY_BUY_LIST = [];

// add weapons and accessories
PONTY_BUY_LIST.push(...[
    "gcape", "bataxe", "lmace",
    "xmace", "basher", "broom",
    "t2bow", "t3bow", "rapier", "crossbow", "harpybow",
    "glolipop", "ololipop",
    "wbook0", "wbook1", "wbookhs",
    "harbringer", "oozingterror", "blaster",
    "fireblade", "dragondagger", "hdagger",
    "scythe", "vsword", "vdagger", "vhammer",
    "dartgun", "mshield",
    "t2quiver", "exoarm",
    "lunarmace",
	//"wshoes", "wcap", "coat1", "shoes1", "helmet1",
]);

// add accessories
PONTY_BUY_LIST.push(...[
    "dexearring", "strearring", "intearring",
    "rabbitsfoot", "vring", "solitaire", 
    "suckerpunch", "ringofluck", "ringhs", "goldring", "zapper", "trigger",
    "strbelt", "intbelt", "dexbelt", "sbelt", "mbelt",
    "intamulet", "t2intamulet", "t2stramulet", "t2dexamulet",
    "vorb", "strring",
    "orbofstr", "orbofdex", "orbofint",
    "resistancering", "armorring",
    "cring", "cearring", "lostearring",
    "molesteeth", "mearring",
    "snring", "amuletofm", "bfangamulet",
    "sanguine", "mpxamulet", "mpxbelt", "northstar",
    "talkingskull", "skullamulet", //"stramulet",
	"intring","dexring","quiver","intamulet",
	"intearring","dexearring", "dexamulet",//'ringsj',
	"strring", "warmscarf1", "frozenkey",
]);

// add equipment
PONTY_BUY_LIST.push(...[
    "mittens", "supermittens", "angelwings",
    "cyber", "glitch", "fury",
    //"wcap", "wbreeches", "wshoes", "wattire", "wgloves",
    "fcape", "vcape", "stealthcape",
    "wingedboots", "ecape",
]);

// add materials to the list
PONTY_BUY_LIST.push(...[
    //"spores", "cclaw",
    "poison", "ink", "snakefang",
    "carrot", "spidersilk",
    "bfur", "lotusf", "funtoken",
    "leather", "feather0", "feather1",
    "drapes", "ascale", "pstem",
    "essenceoffrost", //"essenceoffire",
    "essenceoflife", "essenceofgreed",
    "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8",
    "beewings", "spores",
    "btusk", "bfang", "bwing",
    "goldenegg",
    "cscale", "cshell", "candy0", "candy1",
	// EASTER
	"egg0","egg1", "egg2", "egg3","egg4",
	"egg5","egg6","egg7","egg8",
]);

// Manual init shit
// has to be after ponty buy list
// This is the stuff that happens depending on where code is init'd
if(character.map === "main") {
	smart_move({ x: -195 , y: -111, map: 'main'});
	buy_from_ponty();
}
if(character.map === "woffice") {
	buy_from_goblin();
	smart_move({ x: -195 , y: -111, map: 'main'});
	
}


function custom_log(message) {
    if (!ENABLE_LOGS) return;
    log(message)
}

function buy_modifiers_from_npc(npc_name, items_to_buy=[]) {

    custom_log(`Buying from: ${npc_name}`);

    // not if we're in the bank
    if (character.map.startsWith("bank")) return;

    // npc_name - ponty or goblin
    // items_to_buy - the list of items to look for
    let buy_command = {
        'secondhands': 'sbuy',
        'lostandfound': 'sbuy'
    }

    // how many spaces available to buy stuff with
    let emptySpaces = character.esize - 1;

    // set up handler to receive the emit
    parent.socket.once(`${npc_name}`, data => {
		//if(npc_name === "lostandfound") set("goblinData", data);
        for (let d of data) {
            // dont buy more stuff if we're full
            if (!emptySpaces) {
                custom_log("too full skipping")
                continue
            }
            if (["carrotsword","cdragon"].includes(d.name)) continue;
            if (d.p) {
                custom_log(`${d.name} has modifier: ${d.rid}`)
                parent.socket.emit(buy_command[npc_name], {
                    "rid": d.rid,
                    ...(npc_name === "lostandfound" && {f: true})
                }, 1)
                emptySpaces -= 1;
            }

            if (items_to_buy.includes(d.name)) {
                parent.socket.emit(buy_command[npc_name], {
                    "rid": d.rid,
                    ...(npc_name === "lostandfound" && {f: true})
                }, 1)
                emptySpaces -= 1;
            }

            if (DESTROY_13_ENABLED) {
                let item = G.items[d.name];
                if (item?.g < DESTROY_13_THRESHOLD && item?.upgrade) {
                    if (item?.level < 13)  continue;
                    parent.socket.emit(buy_command[npc_name], {
                        "rid": d.rid,
                        ...(npc_name === "lostandfound" && {f: true})
                    }, 1);
                }
            }
        }
    });

    parent.socket.emit(`${npc_name}`);
}

////////// GOBLIN

async function move_to_goblin() {
	set_message("Move Gobbo");
	custom_log("Move Gobbo");
	await smart_move("woffice");
}

async function move_to_main() {
	set_message("Move Main");
	custom_log("Move Main");
	await smart_move({ x: -3 , y: -3, map: 'main'})
}

function buy_from_goblin(items_to_buy=PONTY_BUY_LIST) {
    if (character.map !== "woffice") {
        custom_log("not in woffice");
        return;
    }
    
    if (character.s.hopsickness) {
        custom_log("cannot purchase while sick");
        return;
    }
    
    items_to_buy.push(...["offeringp"])
	set_message("Gobbo Check");
    buy_modifiers_from_npc("lostandfound", items_to_buy)
}

/// PONTY

function buy_from_ponty(items_to_buy=PONTY_BUY_LIST) {
    if (!ENABLE_PONTY) return
    if (character.map !== "main") {
        custom_log("cannot buy from ponty while not in main map");
        return;
    }
	set_message("Ponty Chk");
    buy_modifiers_from_npc("secondhands", items_to_buy)
}

setInterval(function(){
	buy_from_ponty();
},PONTY_TIMEOUT);

if(enableGoblinCheck) {
	setInterval(async function(){
		set_message("Gobbo Proc");
		custom_log("Gobbo Proc");
		await move_to_goblin();
		await buy_from_goblin();
		await move_to_main();
	},GOBLIN_TIMEOUT);
}

///////////////////////// COMPOUND //////////////////////

var compound_whitelist = ['wbook0', 'intamulet', 'stramulet', 'dexamulet', 'intearring', 'strearring', 'dexearring', 'hpbelt', 'hpamulet', 'ringsj', 'amuletofm', 'orbofstr', 'orbofint', 'orbofres', 'orbofhp', 'dexring', 'intring', 'strring', 'skullamulet', 'dexbelt','ringsj','strbelt','intbelt',//'vitring',
						 ];
var use_better_scrolls = false; //240,000 Gold Scroll = true [only will use for +2 and higher], 6,400 Gold Scroll = false [will only use base scroll no matter what]
var maxLevel = 3;
//compound settings

if (enableCompound) {
	setInterval(async function() {
		//if(currentAction === "fishing" || currentAction === "mining") return;
		set_message("Compounding");
		await compound_items();
	}, COMPOUND_TIMEOUT); 
}

async function compound_items() {
  let to_compound = character.items.reduce((collection, item, index) => {
    if (item && item.level < maxLevel && compound_whitelist.includes(item.name)) {
      let key = item.name + item.level;
      !collection.has(key) ? collection.set(key, [item.level, index]) : collection.get(key).push(index);
    }
    return collection;
  }, new Map());

  for (var c of to_compound.values()) {
    let scroll_name = use_better_scrolls && c[0] > 1 ? 'cscroll1' : 'cscroll0';

    for (let i = 1; i + 2 < c.length; i += 3) {
      let [scroll, _] = find_item(i => i.name == scroll_name);
      if (scroll == -1) {
        parent.buy(scroll_name);
        return;
      }
      parent.socket.emit('compound', {
        items: [c[i], c[i + 1], c[i + 2]],
        scroll_num: scroll,
        offering_num: null,
        clevel: c[0]
      });
    }
  }
}

function find_item(filter) {
  for (let i = 0; i < character.items.length; i++) {
    let item = character.items[i];

    if (item && filter(item))
      return [i, character.items[i]];
  }

  return [-1, null];
}

/////////////// UPGRADES //////////////////
var emaxlevel = 7; //Max level it will stop upgrading items at if enabled
var whitelist = ['wgloves', 'wbreeches', 'cupid', 'wcap', 'wattire', 'wshoes',
				'pants1','gloves1', 'coat1','shoes1', 'helmet1', 'glolipop', 'ecape',]; 

if(enableUpgrades) {
	setInterval(async function() {
		if (can_use("massproduction")) use_skill("massproduction")
		//if(currentAction === "fishing" || currentAction === "mining") return;
		await upgrade(emaxlevel);
	}, UPGRADE_TIMEOUT);
}

async function upgrade(level) {
	set_message("UPGRADING");
  for (let i = 0; i < character.items.length; i++) {
    let c = character.items[i];
    if (c && whitelist.includes(c.name) && c.level < level) {
      let grades = get_grade(c);
      let scrollname;
      if (c.level < grades[0])
        scrollname = 'scroll0';
      else if (c.level < grades[1])
        scrollname = 'scroll1';
      else
        scrollname = 'scroll2';

      let [scroll_slot, scroll] = find_item(i => i.name == scrollname);
      if (!scroll) {
		await smart_move({ x: -195 , y: -111, map: 'main'});
        parent.buy(scrollname);
        return;
      }
		if (can_use("massproduction")) use_skill("massproduction")
      parent.socket.emit('upgrade', {
        item_num: i,
        scroll_num: scroll_slot,
        offering_num: null,
        clevel: c.level
      });
      return;
    }
  }
}

function get_grade(item) {
  return parent.G.items[item.name].grades;
}

// Returns the item slot and the item given the slot to start from and a filter.
function find_item(filter) {
  for (let i = 0; i < character.items.length; i++) {
    let item = character.items[i];

    if (item && filter(item))
      return [i, character.items[i]];
  }

  return [-1, null];
}

/*
* TOOLS
*/

function buy_tool(tool_name) {
    for (let slot of Object.keys(character.slots)) {
        if (slot.includes("trade") && !character.slots[slot]) {
            parent.socket.emit("trade_wishlist",
                {
                    q: 1,
                    slot: slot,
                    price: 1000000,
                    level: 0,
                    name: tool_name
                }
            );
            set_message("Wishlist Setup");
            return;
        }
    }
}

function check_for_tools() {
    let hasRod = false;
    let hasPickaxe = false;
    let hasRodWish = false;
    let hasPickaxeWish = false;
    for (let item of character.items) {
        if (item?.name == "pickaxe") {
            hasPickaxe = true;
        }

        if (item?.name == "rod") {
            hasRod = true;
        }
    }

    for (let slot of Object.keys(character.slots)) {
        let item = character.slots[slot];
        if (item?.name == "pickaxe") {
            hasPickaxeWish = true;
        }
        if (item?.name == "rod") {
            hasRodWish = true;
        }
    }
    if (!hasRod && !hasRodWish) buy_tool("rod");
    if (!hasPickaxe && !hasPickaxeWish) buy_tool("pickaxe");
}

if (ENABLE_BUY_TOOLS) {
    check_for_tools()
    setInterval(check_for_tools, 60000);
}

/*
* SELL STUFF AUTO
*/

setInterval(function() {

  //sells items in whitelist
  if (sItem) {
    sellItem()
  }

}, 1000);

function sellItem() {
  for (let i = 0; i < character.items.length; i++) {
    let c = character.items[i];
    if (c) {
      if (c && sellWhitelist.includes(c.name)) {

        sell(i);
      }
    }
  }
}

/*
* BANK API
*/

const UpdateEarthBank = async () => {
const ALDATA_KEY=escape("YOURKEY");
	const url = `https://aldata.earthiverse.ca/bank/${character.owner}/${ALDATA_KEY}`
      let bankData = { ...character.bank };
    for (let key in bankData)  {
        let bankSlot = bankData[key];
        // remove nulls
        if (Array.isArray(bankSlot)) {
            bankData[key] = bankSlot.filter(item => item)
        }
    }
    
    if (!character.stand) {
        await open_stand()
    }

    // stringify the data
    data = JSON.stringify({
        ...bankData,
        items47: { ...character.slots }
    });
    
    // create our request
    settings = {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: data
    };

    // if response.status == 200, it was successfully updated
    fetch(url, settings).then(response => console.log(response))
}

Destroying Stuff (1/1000000 chance at +13)

function destroy(inventoryIndex, itemQuantity=1) {
  // take an inventory index and optional quantity and
  // emit a socket event to destroy the item(s)

  // invalid inventory index
  // Note: probably not needed if the server does the check anyway
  // though there may be value in reducing latency overhead by checking
  // here on the client
  if (inventoryIndex < 0 || inventoryIndex > 41) return;

  // !NaN => true; valid number required
  if (!parseInt(itemQuantity)) return;

  // quantity must be between 1 and 9999
  if (itemQuantity < 1 || itemQuantity > 9999) return;

  // define what we're destroy & how many
  let destroyPayload = {
    num: inventoryIndex,
    q: itemQuantity,
    statue: true,
  }

  // emit the destruction of the item
  parent.socket.emit("destroy", destroyPayload);
}
socket.on("destroy", function (data) {
  var player = players[socket.id];
  var add = "+nc+inv";
  data.num = max(0, parseInt(data.num) || 0);
  if (!player.items[data.num]) {
    return fail_response("no_item", { num: data.num });
  }
  var item = player.items[data.num];
  var name = player.items[data.num].name;
  data.q = min(max(parseInt(data.q) || 0, 1), (item && item.q) || 1);
  if (item.name == "placeholder") {
    return fail_response("item_placeholder", { num: data.num });
  }
  if (item.l) {
    return fail_response("item_locked", { num: data.num });
  }
  if (item.b) {
    return fail_response("item_blocked", { num: data.num });
  }
  if (item.level != 13) {
    consume(player, data.num, data.q);
    //player.items[data.num]=player.citems[data.num]=null;
  }
  if (data.statue) {
    if (item.name == "shadowstone") {
      add = "+u+cid";
      player.s.invis = { ms: 99999 };
    }
    if (G.items[item.name].upgrade && Math.random() < 1.0 / ((gameplay == "hardcore" && 10000) || 1000000)) {            <--- THIS SECTION
      add = "+u+cid";
      item.level = 13;
      player.items[data.num] = item;
      player.citems[data.num] = cache_item(player.items[data.num]);
      const announce = !item.silent;
      if (announce && !player.stealth) {
        broadcast("server_message", {
          message: player.name + " received " + item_to_phrase(item),
          color: colors.server_success,
          item: cache_item(item),
          type: "server_usuccess",
          name: player.name,
        });
      }
    }
    xy_emit(G.maps.spookytown.ref.poof, "upgrade", { type: "poof", success: 1 });
  }
  resend(player, "reopen" + add);
  success_response("destroyed", { name: name, num: data.num, cevent: "destroy" });
});

var destroyItemID = locate_item("wbreeches");
destroy(destroyItemID)