Skip to content

Instantly share code, notes, and snippets.

@MrCelticFox
Last active March 23, 2023 02:43
Show Gist options
  • Save MrCelticFox/cf416fddbc25c456de5bbe5d1aac857f to your computer and use it in GitHub Desktop.
Save MrCelticFox/cf416fddbc25c456de5bbe5d1aac857f to your computer and use it in GitHub Desktop.
An updated and improved auto-sell script for TownStar

Improved TownStar Auto-Sell Script

Disclaimer

At the time of writing scripts are allowed to be used with TownStar, as long as they don't do anything that breaks the game, but double check what the rules say to be sure: https://support.gala.games/town-star-rules

Use at your own risk, I have tested this and am satisfied it is free of bugs, but I am not responsible for any loss incurred by others that arose from using this script.

Now for the fun stuff...

 

How to use

This script is designed to be used with Tampermonkey, if you haven't used this extension before then please first install it. Then you can just create a new script through Tampermonkey and paste the contents of updated_sell_script.js into it. Then open or refresh TownStar and it should load. If you open the developper tools you will see some info messages in the console.

This script supports both Trade Depots and Trade Piers. It does NOT currently support Freight Piers or Express Depots (though I might add it in future) If you want to use a Freight Pier you can always use the original script for that one item, or you can experiment with adding bits of your own code.

However, it does support any number of Trade Depots and Trade Piers (including a mix of both) and will use them all one after another in a loop! This is automatic and requires no configuration from the user.

Point to note: when you first load the game (or after a page refresh), you should manually open a trade terminal on each edge of your town, just to let the trades load for that side or it might cause the script to glith on the first trade (if all your depots are on one side then you only need to do one)

There are 3 things you need to configure:

  • sellTimer: the optimal value is given by calculating longest_journey_time/number_of_trade_terminals (and it's better to always round up a bit)
  • gasLimit: the minimum amount of gas you want to keep (in case you want some spare for manual sales). Must also be enough for at least one trade (so 0 is not ok!)
  • sellItemsAndLimits: the most important part, here you should add every item you want to be sold automatically and a threshold value. The threshold is the minimum amount you want to keep, so the script will only sell when you have threshold+10 of that item. See Examples section for more details.

Thats's it! Simply configure those variables and you should be good to go (remember to refresh the game to load any changes you made to the script).

 

Examples

To illustrate the script's full functionality, here are some examples.

Example 1

I'm running a wine build and also selling grapes for cash, I have two Trade Depots and travel time is 35secs at a cost of 1 fuel (lucky me!). I also sometimes have a surplus of Oak Wood but I want to keep between 10 and 20 of them. Everything else is assumed to be balanced for this example. Here is my config:

const sellTimer = 20; // 35/2 = 17.5, and round up to 20 just to be safe
const gasLimit = 1; // I don't want to keep any extra
const sellItemsAndLimits = [
    ['Wine', 0], // Don't keep any wine (it will sell when I have > 0+9 wine, so 10)
    ['Pinot_Noir_Grapes', 20], // I want to keep at least 20, so this will sell anything over 29
    ['Oak_Wood', 10] // I want to keep between 10 and 20, so anything over 19 will be sold
]; 

Example 2

I'm running a Pumpkin Pie build, I have 3 Trade Piers and 2 Trade Depots, my longest travel time is 3mins (so 180secs) and fuel cost is 2. I'm selling grapes for cash but I also sometimes have surplus of wood, gas, eggs, pumpkins, etc. I want to keep a few of each though so I can always make my pies. Here is my config:

const sellTimer = 40; // 180/5 = 36, going to round up to 40 to be safe
const gasLimit = 2; // I don't want to keep any extra but my limit is 2 fuel per trip
const sellItemsAndLimits = [
    ['Wood', 45], // sell when I have 55 or more
    ['Gasoline', 65], // sell when I have 75 or more
    ['Pinot_Noir_Grapes', 0], // sell as soon as I can
    ['Wheat', 20], // want to keep at least 20, sell when 30 or more
    ['Eggs', 15], // want to keep at least 15, sell when 25 or more
    ['Sugarcane', 20], // same as wheat, you get the idea
    ['Pumpkin', 20], // same as above
    ['Pumpkin_Pie', 0] // sell pies as soon as I have at least 10
]; 

Example 3

I have only 1 Trade Pier with a time of only 15secs and 1 gas cost. I do want to keep some extra gas for myself though, as I will manually use a Freight Pier sometimes which isn't handled by this script. The only things I want to sell automatically are blue steel and surplus cotton yarn. I also want to save some blue steel for the last day of the competition, so I will keep 2 stacks and sell the rest. Here is my config:

const sellTimer = 20; // 15/1 = 15, but going to round up anyway
const gasLimit = 10; // trip cost is 1, but I want to keep a few for manual sells
const sellItemsAndLimits = [
    ['Cotton_Yarn', 5], // keep at least 5, sell when I have 15 or more
    ['Blue_Steel', 20] // I want to keep 2 stacks of 10, so this will only sell when I have 30 or more, I will never drop under 20
]; 
// ==UserScript==
// @name Improved Town Star Auto-Sell
// @namespace http://tampermonkey.net/
// @version 0.1
// @description An improved version of the TownStar Auto-Sell script
// @author MrCelticFox
// @match https://townstar.sandbox-games.com/launch/
// @grant GM_setValue
// @grant GM_listValues
// @grant GM_getValue
// @grant GM_deleteValue
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const sellTimer = 300; // Seconds between selling (best setting is longest journey time divided by number of trade terminals)
const gasLimit = 16; // Minimum amount of gas to keep, script will not sell anything when gas falls below this value
const sellItemsAndLimits = [ // Put each item you want to sell here, and the limit (minimum amount that will NOT be sold)
['Wood', 65],
['Gasoline', 65],
['Pinot_Noir_Grapes', 0],
['Wheat', 20],
['Eggs', 15],
['Sugar', 15],
['Sugarcane', 20],
['Pumpkin', 20],
['Pumpkin_Pie', 0]
]; // All above are examples, you can put as many or as few as you want, and whatever limits you want
let sellItemIndex = 0;
// Updates the index for the items in a circular manner (goes back to the beginning when it hits the end)
function updateSellItemIndex() {
console.log("updating sellItem index..");
sellItemIndex += 1;
if (sellItemIndex >= sellItemsAndLimits.length) {
sellItemIndex = 0;
}
}
let availableDepots = []; // Leave empty, the script will auto-fill this with what's available in game
let depotIndex = 0;
// Updates the index for the depots and piers in a circular manner (goes back to the beginning when it hits the end)
function updateDepotIndex() {
console.log("updating depot index..");
depotIndex += 1;
if (depotIndex >= availableDepots.length) {
depotIndex = 0;
}
}
let sellingActive = 0;
/*
Handles basic game functions like automatically clicking 'play' when the page is refreshed
*/
new MutationObserver(function(mutations){
let airdropcollected = 0;
if(document.getElementsByClassName('hud-jimmy-button')[0] && document.getElementsByClassName('hud-jimmy-button')[0].style.display != 'none'){
document.getElementsByClassName('hud-jimmy-button')[0].click();
document.getElementById('Deliver-Request').getElementsByClassName('yes')[0].click();
}
if(document.getElementsByClassName('hud-airdrop-button')[0] && document.getElementsByClassName('hud-airdrop-button')[0].style.display != 'none'){
if(airdropcollected == 0){
airdropcollected = 1;
document.getElementsByClassName('hud-airdrop-button')[0].click();
document.getElementsByClassName('air-drop')[0].getElementsByClassName('yes')[0].click();
}
}
if (document.getElementById("playnow-container") && document.getElementById("playnow-container").style.visibility !== "hidden") {
document.getElementById("playButton").click();
}
if(typeof Game != 'undefined' && Game.town != null) {
if(sellingActive == 0) {
console.log('Game loaded');
sellingActive = 1;
activateSelling();
//activateTestFunction();
}
}
}).observe(document, {attributes: true, childList: true , subtree: true});
/*
The main selling function
*/
function activateSelling() {
let start = GM_getValue("start", Date.now());
GM_setValue("start", start);
setTimeout(function(){
let tempSpawnCon = Trade.prototype.SpawnConnection;
Trade.prototype.SpawnConnection = function(r) {tempSpawnCon.call(this, r); console.log(r.craftType); GM_setValue(Math.round((Date.now() - start)/1000).toString(), r.craftType);}
},10000);
// load all the trade depots and trade piers into an array
availableDepots = Object.values(Game.town.objectDict).filter(o => (o.type === 'Trade_Pier' || o.type === 'Trade_Depot'));
window.mySellTimer = setInterval(function(){
let depotObj = availableDepots[depotIndex];
let depotKey = "[" + depotObj.townX + ", " + "0, " + depotObj.townZ + "]";
Game.town.objectDict[depotKey].logicObject.OnTapped(); // Collect reward from previous time if there is one
// Must have enough gas first
if (Game.town.GetStoredCrafts()["Gasoline"] > gasLimit) {
// Skip over any items we can't sell because they are below the limit
let tmpCounter = 0;
while(tmpCounter < sellItemsAndLimits.length) {
let tmpAmount = Game.town.GetStoredCrafts()[sellItemsAndLimits[sellItemIndex][0]];
if (typeof tmpAmount != 'undefined' && (tmpAmount > (sellItemsAndLimits[sellItemIndex][1] + 9)) ) {
break;
}
updateSellItemIndex();
tmpCounter++;
}
// Get item to sell and its limit from the array
let sellItem = sellItemsAndLimits[sellItemIndex][0];
let sellLimit = sellItemsAndLimits[sellItemIndex][1];
// Execute the trade (but double check because maybe we don't have enough of anything)
if (Game.town.GetStoredCrafts()[sellItem] > (sellLimit + 9)) {
console.log("SELLING " + sellItem + "!");
Game.app.fire("SellClicked", {x: depotObj.townX, z: depotObj.townZ});
setTimeout(function(){
let craftTarget = document.getElementById("trade-craft-target");
craftTarget.querySelectorAll('[data-name="' + sellItem + '"]')[0].click();
setTimeout(function(){
document.getElementById("destination-target").getElementsByClassName("destination")[0].getElementsByClassName("sell-button")[0].click();
},1000);
},3000);
updateDepotIndex();
updateSellItemIndex();
}
else {
console.log("Not enough items to sell");
}
}
else {
console.log("Not enough fuel");
}
},sellTimer*1000);
}
/*
For running tests while developing
*/
function activateTestFunction() {
}
})();
@peacefmind
Copy link

Script still working with few minor issues:

  1. Will not run with Express Depot, will try to add to available options Express_Depot
    availableDepots = Object.values(Game.town.objectDict).filter(o => (o.type === 'Trade_Pier' || o.type === 'Trade_Depot' || o.type === 'Express_Depot'));
  2. Can't get auto sale for Iron or Silica, any ideas?
  3. For some crazy reason it works only on MS Edge

@TimoBorriss
Copy link

I have an express depot and a standard one and it is working fine. Itt works for silica as well, I haven't tested Iron recently, but a few days ago, it weas no issue. I use brave and the following script:
note: I have not changed anything, but the line 99 change for the freight pier, mentioned above.

// ==UserScript==
// @name Improved Town Star Auto-Sell
// @namespace http://tampermonkey.net/
// @Version 0.1
// @description An improved version of the TownStar Auto-Sell script
// @author MrCelticFox
// @match https://townstar.sandbox-games.com/launch/
// @grant GM_setValue
// @grant GM_listValues
// @grant GM_getValue
// @grant GM_deleteValue
// @run-at document-start
// ==/UserScript==

(function() {
'use strict';
const sellTimer = 20; // Seconds between selling (best setting is longest journey time divided by number of trade terminals)
const gasLimit = 1; // Minimum amount of gas to keep, script will not sell anything when gas falls below this value
const sellItemsAndLimits = [ // Put each item you want to sell here, and the limit (minimum amount that will NOT be sold)
['Wood', 60],
['Lumber', 30],
['Sugar', 0],
['Candy_Canes', 0],
['Peppermint', 5],
['Crude_Oil', 15],
['Gasoline', 30],
['Water', 10],
['Silica', 0],
['Sugarcane',30],
['Pinot_Noir_Grapes',0],
]; // All above are examples, you can put as many or as few as you want, and whatever limits you want

let sellItemIndex = 0;

// Updates the index for the items in a circular manner (goes back to the beginning when it hits the end)
function updateSellItemIndex() {
    console.log("updating sellItem index..");
    sellItemIndex += 1;
    if (sellItemIndex >= sellItemsAndLimits.length) {
        sellItemIndex = 0;
    }
}
let availableDepots = []; // Leave empty, the script will auto-fill this with what's available in game
let depotIndex = 0;
// Updates the index for the depots and piers in a circular manner (goes back to the beginning when it hits the end)
function updateDepotIndex() {
    console.log("updating depot index..");
    depotIndex += 1;
    if (depotIndex >= availableDepots.length) {
        depotIndex = 0;
    }
}
let sellingActive = 0;
/*
Handles basic game functions like automatically clicking 'play' when the page is refreshed
*/
new MutationObserver(function(mutations){
    let airdropcollected = 0;
    if(document.getElementsByClassName('hud-jimmy-button')[0] && document.getElementsByClassName('hud-jimmy-button')[0].style.display != 'none'){
        document.getElementsByClassName('hud-jimmy-button')[0].click();
        document.getElementById('Deliver-Request').getElementsByClassName('yes')[0].click();
    }
    if(document.getElementsByClassName('hud-airdrop-button')[0] && document.getElementsByClassName('hud-airdrop-button')[0].style.display != 'none'){
        if(airdropcollected == 0){
            airdropcollected = 1;
            document.getElementsByClassName('hud-airdrop-button')[0].click();
            document.getElementsByClassName('air-drop')[0].getElementsByClassName('yes')[0].click();
        }
    }
    if (document.getElementById("playnow-container") && document.getElementById("playnow-container").style.visibility !== "hidden") {
        document.getElementById("playButton").click();
    }
    if(typeof Game != 'undefined' && Game.town != null) {
        if(sellingActive == 0) {
          console.log('Game loaded');
          sellingActive = 1;
          activateSelling();
          //activateTestFunction();
        }
    }
}).observe(document, {attributes: true, childList: true , subtree: true});
/*
The main selling function
*/
function activateSelling() {
    let start = GM_getValue("start", Date.now());
    GM_setValue("start", start);
    setTimeout(function(){
        let tempSpawnCon = Trade.prototype.SpawnConnection;
        Trade.prototype.SpawnConnection = function(r) {tempSpawnCon.call(this, r); console.log(r.craftType); GM_setValue(Math.round((Date.now() - start)/1000).toString(), r.craftType);}
    },10000);

    // load all the trade depots and trade piers into an array
    availableDepots = Object.values(Game.town.objectDict).filter(o => (o.type === 'Trade_Pier' || o.type === 'Trade_Depot' || o.type === 'Express_Depot'));

    window.mySellTimer = setInterval(function(){
        let depotObj = availableDepots[depotIndex];
        let depotKey = "[" + depotObj.townX + ", " + "0, " + depotObj.townZ + "]";
        Game.town.objectDict[depotKey].logicObject.OnTapped(); // Collect reward from previous time if there is one

        // Must have enough gas first
        if (Game.town.GetStoredCrafts()["Gasoline"] > gasLimit) {
            // Skip over any items we can't sell because they are below the limit
            let tmpCounter = 0;
            while(tmpCounter < sellItemsAndLimits.length) {
                let tmpAmount = Game.town.GetStoredCrafts()[sellItemsAndLimits[sellItemIndex][0]];
                if (typeof tmpAmount != 'undefined' && (tmpAmount > (sellItemsAndLimits[sellItemIndex][1] + 9)) ) {
                    break;
                }
                updateSellItemIndex();
                tmpCounter++;
            }

            // Get item to sell and its limit from the array
            let sellItem = sellItemsAndLimits[sellItemIndex][0];
            let sellLimit = sellItemsAndLimits[sellItemIndex][1];

            // Execute the trade (but double check because maybe we don't have enough of anything)
            if (Game.town.GetStoredCrafts()[sellItem] > (sellLimit + 9)) {
                console.log("SELLING " + sellItem + "!");
                Game.app.fire("SellClicked", {x: depotObj.townX, z: depotObj.townZ});
                setTimeout(function(){
                    let craftTarget = document.getElementById("trade-craft-target");
                    craftTarget.querySelectorAll('[data-name="' + sellItem + '"]')[0].click();
                    setTimeout(function(){
                        document.getElementById("destination-target").getElementsByClassName("destination")[0].getElementsByClassName("sell-button")[0].click();
                    },1000);
                },3000);
                updateDepotIndex();
                updateSellItemIndex();
            }
            else {
                console.log("Not enough items to sell");
            }
        }
        else {
            console.log("Not enough fuel");
        }
    },sellTimer*1000);
}
/*
For running tests while developing
*/
function activateTestFunction() {
}

})();

@banhao
Copy link

banhao commented Jan 2, 2022

@Thefatnessguru Your information is very important for me. I switched to using the Brave browser then everything is working fine. I tried Chrome, Firefox, Edge, but none can work. I spent too much time playing this game, now I can have more time to do other stuff. Thanks again.

@oggew2
Copy link

oggew2 commented Jan 6, 2022

@Thefatnessguru did you manage to get it working on an android? If so, what browser? Any problems?

@jleangstartup
Copy link

jleangstartup commented Jan 15, 2022

having trouble with this script today. Getting 'Game not defined', 'Trade is not defined', etc. Not sure if its my tampermonkey?
Says No script is Running when i tap on the chrome extension icon, even though its enabled in the other screen

@TimoBorriss
Copy link

@jiangstartup
I have the same issue. maybe the server adresss changed?

@diego-mi
Copy link

if your script not loading anymore change line 7:

This: // @match https://townstar.sandbox-games.com/launch/
to: // @match https://townstar.sandbox-games.com/launch

Remove de /, save and reload the game page

@peacefmind
Copy link

Thanks Diego, I was going crazy... How you figure this out, did gala changed link?

@banhao
Copy link

banhao commented Jan 15, 2022

My problem is using "https://townstar.sandbox-games.com/launch" the page only can load 60%
Only this "https://app.gala.games/games/town-star/play/" can load the page successfully. So how to fix this issue? Thanks!

@TimoBorriss
Copy link

@banhao this has nothing to do with the script. Just close the browser and try again. Sometimes a new tab is even enough.

@banhao
Copy link

banhao commented Jan 15, 2022

@Thefatnessguru Thanks, I'll try and use the other browsers.

@cuddlesandchompy
Copy link

@diego-mi Thanks man. That totally worked!

@jleangstartup
Copy link

Yeah I'm getting the 60% issue too with "https://townstar.sandbox-games.com/launch", and if i change the script to "https://app.gala.games/games/town-star/play/" and use that url to navigate, it doesnt seem to load the script.

@freezeza
Copy link

https://gist.github.com/MrCelticFox/cf416fddbc25c456de5bbe5d1aac857f#gistcomment-4029846

it's work.i m stuck at 60% and try chance website and anymore are don't working.but i try him it's working.thank you

@MrCelticFox
Copy link
Author

As others have said, the 60% issue is unrelated to the script, it's been an occasional bug with the game for a long time, you just need to open a new tab one or more times until it loads. (I used to get this issue before I ever used a script).

@arpanagr
Copy link

Its not working on MAC chrome version 97.0.4692.71 (Official Build) (arm64) with tamperMonkey.
The script doesn't event start since I don't see the same in tamperMonkey window.
Can someone help?

@TimoBorriss
Copy link

@arpanagr try deleting the "launch" as well

@arpanagr
Copy link

I have changed the match to this as this is the url I am opening:

@match https://app.gala.games/games/town-star/play/

Let me try with old url without launch

@arpanagr
Copy link

Ok did a bit of debugging and finally found that the URL from which this is redirected is: https://townstar.sandbox-games.com/launch
After changing the url now it works like charm.

Had some issues with item names. It would be great if someone can list all the item names. Like in example it was given water but the actual name is 'Water_Drum'.

@MrCelticFox
Copy link
Author

@arpanagr there is no Water in my examples 🤔

You can find a list of all the items in game here:
https://townstar.guide/crafts.html

To use them in the script take the full name and just replace spaces with underscore (_)

@Pachangito2
Copy link

I can't seem to get it to work unfortunately. Got it into Tapermonkey, edited my auto sells, loaded the game, clicked on extension to enable the script, but it doesn't do anything. Am I missing something?

@MrCelticFox
Copy link
Author

@Pachangito2 If I remember correctly (been a while since I played) you need to enable the script, then refresh/load the game.

@arpanagr
Copy link

@Pachangito2 Check for the URL in the @match section, it should be https://townstar.sandbox-games.com/launch

Then check tampermonkey extension after the loading the game whether it has started the script or not.

If the script is started then check the exact name of the item you are trying to sell.

@oggew2
Copy link

oggew2 commented Jan 21, 2022

Change the script on the 7th line to this:
// @match *://*.sandbox-games.com/*
Had problems a few days ago but now it's working fine

@engFelipeMonteiro
Copy link

I am tring to make this work again, but it don't.
I already changed the @match, and nothing.
The script still working for you?
I am using
OS: MacOS Big Sur
Chrome: Version 98.0.4758.80 (x86_64)
tampermonkey 4.13

@TurTedric
Copy link

Anyone using this script? Can't put it to work

@kcb3
Copy link

kcb3 commented Feb 22, 2022

I got it working again using this script: https://gala-analytics.herokuapp.com/static/townstar_auto_sell_v2.txt
I comes from V2 here: https://gala-analytics.herokuapp.com/townstar

I also made the modification from above: line 7 => // @match https://townstar.sandbox-games.com/launch
You need to get rid of the / at the end.

It is working fine now :-)

@TurTedric
Copy link

Thanks for the help.
I've tried it also, but on both scripts i get the same error message: Game undefined
error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment