// ==UserScript==
// @name FarmRPG Helper
// @namespace https://gf.qytechs.cn/users/1114461
// @version 0.021
// @description QOL and helper scripts for FarmRPG
// @author Fewfre
// @license GNU GPLv3
// @match https://farmrpg.com/index.php
// @icon https://www.google.com/s2/favicons?sz=64&domain=farmrpg.com
// @grant none
// ==/UserScript==
(function($, app) {
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
// time in seconds
async function sleep(time, max) {
return new Promise(resolve=>{
setTimeout(resolve, max ? randomNumber(time*1000, max*1000) : time*1000);
});
}
// https://stackoverflow.com/a/61511955
function waitForElm(selector, options={}) {
return new Promise((resolve, reject) => {
if ($(selector).is(':visible') && $(selector).css('opacity') > 0.1) {
return resolve($(selector+':visible').first()[0]);
}
const observer = new MutationObserver(mutations => {
console.log(selector, $(selector).is(':visible'), $(selector+':visible').first()[0]);
if ($(selector).is(':visible') && $(selector).css('opacity') > 0.1) {
resolve($(selector+':visible').first()[0]);
observer.disconnect();
}
});
if(options.timeout) {
sleep(options.timeout).then(()=>{
observer.disconnect();
reject(new Error("observer timed out"));
});
}
observer.observe(options.target || document.body, {
attributes: true,
childList: true,
subtree: true,
...options.config
});
});
}
/////////////////////////////
// Assets
/////////////////////////////
// uri conversion: https://dopiaza.org/tools/datauri/index.php
// https://pixabay.com/sound-effects/finished-45049/
const SOUND_FINISHED = new Audio("data:audio/mpeg;base64,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XQDigEAQOE61LX5iKKY6TEFNRTMuMTAwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+OIZAAAAAGkAOAAAAAAA0gBwAAATEFNRTMuMTAwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqTEFNRTMuMTAwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq");
/////////////////////////////
// Fishing
/////////////////////////////
const TEXT_FISHING_START = "🤖 AUTO FISH";
const TEXT_FISHING_STOP = "🤖❌ STOP FISHING";
const TEXT_FISHING_STOPPING = "🤖❌ STOPPING";
function getBaitCount() {
return parseInt($("#baitarea strong").first().text() || 0);
}
async function clickFish() {
if(!$("#fishinwater").length) {
throw new Error("No fishing area detected")
}
const fish = await waitForElm(".fish.catch");
fish.click();
/* var triesLeft = 500;
while (triesLeft-- > 0) {
if($(".fish.catch").length) {
$(".fish.catch").click();
return;
}
await sleep(0.05);
} */
/* if(triesLeft <= 0) {
throw new Error("Script gave up, couldn't find fish")
} */
}
async function catchFish() {
if(!$(".fishcaught").length) {
throw new Error("Cannot find fishing to catch")
}
$(".fishcaught").trigger("click");
}
async function fishOne() {
await clickFish();
// We wait for catch modal - but if it doesn't open then we missed the fish and try again
try {
await waitForElm(".picker-catch.modal-in", { timeout:0.1 });
} catch(err) {
if(!fishing) { return; }
return await fishOne();
};
await sleep(0.33); // time for modal to animate in + small buffer
if(!fishing) { catchFish(); return; }
await sleep(0.23, 0.888);
await catchFish();
await sleep(0.05, 0.115); // tiny delay before clicking next fish
}
let fishing = false;
async function startFishing() {
fishing = true;
try {
if(!$("#fishinwater").length) {
throw new Error("No fishing area detected");
}
while (getBaitCount() > 0) {
await fishOne();
if(!fishing) {
break;
}
await sleep(0.3, 0.4);
}
fishing = false;
SOUND_FINISHED.play();
$("#pogfishing").html(TEXT_FISHING_START);
$("#pogfishing").removeAttr('disabled');
}
catch(e) {
alert(e.message);
}
}
function initFishing() {
app.onPageInit("fishing", ({ container })=>{
$(`<button id="pogfishing">${TEXT_FISHING_START}</button>`).appendTo(container.querySelector(".buttons-row")).on("click", function(){
if($("#pogfishing").attr('disabled')) { return; }
if(!fishing) {
$("#pogfishing").html(TEXT_FISHING_STOP);
startFishing();
} else {
$("#pogfishing").html(TEXT_FISHING_STOPPING);
$("#pogfishing").attr('disabled','disabled');
fishing = false;
}
});
});
}
/////////////////////////////
// Exploration
/////////////////////////////
const TEXT_EXPLORING_START = "AUTO EXPLORE";
const TEXT_EXPLORING_STOP = "❌ STOP EXPLORING";
const TEXT_EXPLORING_STOPPING = "❌ STOPPING";
function getStaminaCount() {
return parseInt($("#stamina").text() || 0)
}
async function exploreOne() {
if(!$(".explorebtn").length) {
throw new Error("No exploration area detected");
}
$(".explorebtn").trigger("click");
}
let exploring = false;
async function startExploring() {
exploring = true;
try {
if(!$(".explorebtn").length) {
throw new Error("No exploration area detected");
}
while (getStaminaCount() > 0) {
await exploreOne();
if(!exploring) { break; }
await sleep(0.12, 0.28);
}
exploring = false;
SOUND_FINISHED.play();
$("#pogexploring .item-inner").html(TEXT_EXPLORING_START);
$("#pogexploring").removeAttr('disabled');
}
catch(e) {
alert(e.message);
}
}
function initExploring() {
app.onPageInit("area", ({ container })=>{
$(`<li>
<div class="item-content" style="cursor:pointer" id="pogexploring">
<div class="item-media">🤖</div>
<div class="item-inner">${TEXT_EXPLORING_START}</div>
</div>
</li>`).insertAfter(container.querySelector("li:has(.explorebtn)")).on("click", function(){
if($("#pogexploring").attr('disabled')) { return; }
if(!exploring) {
$("#pogexploring .item-inner").html(TEXT_EXPLORING_STOP);
startExploring();
} else {
$("#pogexploring .item-inner").html(TEXT_EXPLORING_STOPPING);
$("#pogexploring").attr('disabled','disabled');
exploring = false;
}
});
});
}
/////////////////////////////
// Navigation
/////////////////////////////
function initShortcuts() {
$(`<li>
<div class="item-content">
<div class="item-inner">
<div style="display:grid; grid-template-columns: 1fr auto;">
<div><i class="fa fa-fw fa-lightbulb-o" /></div>
<div>
<div class="item-title"> Shortcuts</div>
<div style="font-size: 14px;">${[
{
links: [
{ link:'xfarm', text:'Farm', params:{ id: $('.view-main a[href^="xfarm.php?id="]').attr('href').match(/\?id=(\d*)/)[1] } },
{ link:'explore', text:'Explore' },
{ link:'fish', text:'Fishing' },
{ link:'town', text:'Town' },
{ link:'quests', text:'Help' },
{ link:'workshop', text:'Workshop' },
]
},
{
section: 'Town',
links: [
{ link:'store', text:'Store' },
{ link:'market', text:'Sell' },
{ link:'bank', text:'Bank' },
{ link:'postoffice', text:'Mail' },
{ link:'pets', text:'Pets' },
{ link:'supply', text:'Upgrade' },
{ link:'locksmith', text:'Locksmith' },
{ link:'steakmarket', text:'Steak' },
]
},
{
section: 'Daily',
links: [
{ link:'daily', text:'Chores' },
{ link:'well', text:'Well' },
{ link:'crack', text:'Vault' },
{ link:'comm', text:'Community Center' },
]
},
].map(sctn=>`<div>
${sctn.section ? `<div style="margin-top:3px;"><strong>${sctn.section}</strong></div>` : ''}
${sctn.links.map(l=>`<a href='${l.link}.php${l.params ? '?'+new URLSearchParams(l.params).toString() : ''}' data-view=".view-main">${l.text}</a>`).join(` • `)}
</div>`).join('')}</div>
</div>
</div>
</div>
</div>
</li>`).insertAfter('.view.view-left.navbar-through .page-content li:first-of-type');
}
/////////////////////////////
// Initialize
/////////////////////////////
function init() {
initShortcuts();
initFishing();
initExploring();
// Stop any automation scripts after you change an area
app.onPageInit("area", ({ container })=>{
fishing = false;
exploring = false;
});
}
init();
})(window.jQuery, window.myApp);