Better Rrolf

record game crafts

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Better Rrolf
// @name:zh-CN   Better Rrolf
// @namespace    http://tampermonkey.net/
// @version      1.07
// @description  record game crafts
// @description:zh-CN   record game crafts
// @author       NetheriteJam
// @match        https://rrolf.io/
// @run-at		 document-start
// @license		 MIT
// @grant        none
// ==/UserScript==

const color = {
	default: '#6137BD',
	red: '#FF002F',
	green: '#00FF6F',
}

const types = [
	'basic',
	'pellet',
	'rock',
	'spikes',
	'light',
	'missile',
	'peas',
	'leaf',
	'egg',
	'magnet',
	'uranium',
	'feather',
	'azalea',
	'bone',
	'web',
	'seed',
	'gravel',
	'club',
	'crest',
	'droplet',
	'beak',
	'lightning',
];

const rarities = [
	'common',
	'uncommon',
	'rare',
	'epic',
	'legendary',
	'mythic',
	'exotic',
];

const craftP = [
	0.5,
	0.3,
	0.15,
	0.05,
	0.03,
	0.01,
]

const craftC = [
	0.3021030253,
	0.1189491927,
	0.0322209144,
	0.0038016583,
	0.0013861777,
	0.0001560417,
]

const consoleLogPrefix = '[BetterRrolf] ';

const consoleHeading = 'Better Rrolf by NetheriteJam'

let enabled = true; // if the console is enabled

let input; // the console

let state = 0; // state of console
let selectedOption;

// window.localStorage.setItem('betterRrolf_settings', 'null');
// window.localStorage.setItem('betterRrolf_craft_record', 'null');

let settings = window.localStorage.getItem('betterRrolf_settings'); // get stored craft records

let craftRecord = window.localStorage.getItem('betterRrolf_craft_record'); // get stored craft records

let selectedRarity, selectedType;

let qRarity, qType;

let listPage, pageLength = 20;

function getLocalStorage() {
	if ( settings == 'null' ) {
		settings = [
			false, // abbrCheckWhenLogging
			false, // focusEventLogging
		];
	} else {
		settings = JSON.parse(settings);
	}

	if ( craftRecord == 'null' ) {
		craftRecord = [];
	} else {
		craftRecord = JSON.parse(craftRecord);
	}
}

log(consoleHeading);
log('Loading...');
log('Refresh the page if it doesn\'t load.');
getLocalStorage();
betterRrolf();
// window.addEventListener('load', betterRrolf);

async function betterRrolf() {
	await new Promise(resolve => setTimeout(resolve, 1000));
	initInputBox();
	initDependencies();
	log('Finished loading.');
	log('Execute command \'help\' for more information.');
	log('Press [/] to toggle BetterRrolf console');
	log('Press [.] to focus on BetterRrolf console.');
	window.addEventListener('keydown', e => {
		if ( e.code == 'Slash' ) {
			clr();
			if ( enabled ) {
				log('Disabled console.');
				log('Press [/] to toggle BetterRrolf console');
			} else {
				log('Enabled console.');
				log('Press [/] to toggle BetterRrolf console');
				log('Press [.] to focus on BetterRrolf console.');
				log('Execute command \'help\' for more information.');
			}
			toggleInputBox();
		} else if ( e.code == 'Period' ) {
			if ( settings[1] ) {
				log('Focused on console.');
			}
			input.focus();
			e.preventDefault();
		}
	});
	input.addEventListener('keydown', e => {
		if ( e.code == 'Enter' ) {
			let cmd = input.value;
			input.value = '';
			if ( cmd !== '' ) {
				if ( state == 0 ) { // waiting
					if ( cmd == 'help' ) {
						clr();
						log(`Executed command '${cmd}'`);
						log("");
						help();
					} else if ( cmd == 'set' ) {
						clr();
						log(`Executed command '${cmd}'`);
						log("");
						set();
					} else if ( cmd == 'clr' ) {
						clr();
						log(`Executed command '${cmd}'`);
						log('Execute command \'help\' for more information.');
						log("");
					} else if ( cmd == 'log' ) {
						clr();
						log(`Executed command '${cmd}'`);
						log("");
						log('Input rarity or type to select that of the craft.');
						log('Then input the number of petals you lose.');
						log('Input 5 for a successful craft.');
						log('You DON\'T need to select the rarity and type every time you input the number above.');
						log('You can only select them when you start to craft petals of a new type or rarity.');
						log('Use \'exit\' to exit.');
						state = 3;
						selectedRarity = undefined;
						selectedType = undefined;
					} else if ( cmd == 'q' ) {
						clr();
						log(`Executed command '${cmd}'`);
						log("");
						qRarity = -1;
						qType = -1;
						state = 4;
						q();
					} else if ( cmd == 'list' ) {
						clr();
						log(`Executed command '${cmd}'`);
						log("");
						listPage = 1;
						state = 7;
						list();
					}
				} else if ( state == 1 ) { // select id of an option in settings
					if ( cmd == '0' ) {
						state = 0;
						clr();
						log('Better Rrolf by Netheritejam');
						log('Execute command \'help\' for more information.');
						log('Press [/] to toggle BetterRrolf console');
					} else {
						selectedOption = parseInt(cmd) - 1;
						if ( settings[selectedOption] === undefined ) {
							clr();
							log(`Invalid option ID '${cmd}'`, 'red');
							set();
						} else {
							log('Input the value you want to set this option to:');
							state = 2;
						}
					}
				} else if ( state == 2 ) { // change value of selected option in settings
					if ( selectedOption == 0 || selectedOption == 1 ) {
						if ( cmd == "true" || cmd == "false" ) {
							settings[selectedOption] = (cmd == "true" ? true : false);
							state = 0;
							clr();
							console.log(settings);
							log('Settings has been updated.');
							set();
						} else {
							log('Invalid value', 'red');
						}
					}
				} else if ( state == 3 ) { // log crafts
					if ( cmd == 'exit' ) {
						log('Exited.');
						log('Execute command \'help\' for more information.');
						state = 0;
					} else if ( parseInt(cmd) !== NaN && parseInt(cmd) > 0 && parseInt(cmd) < 6 ) {
						if ( selectedRarity === undefined || selectedType === undefined ) {
							if ( selectedRarity === undefined )
								log('Rarity not selected.', 'red');
							if ( selectedType === undefined )
								log('Type not selected.', 'red');
						} else {
							log(`Recorded craft ${rarities[selectedRarity]} ${types[selectedType]} at ${moment().format('YYYY-MM-DD hh:mm:ss')}.`);
							if ( parseInt(cmd) == 5 )
								log('Successful', 'green');
							else
								log('Unsuccessful', 'red');
							craftRecord.push({
								rarity: selectedRarity,
								type: selectedType,
								cost: parseInt(cmd),
								state: ( parseInt(cmd) == 5 ? true : false ),
								date: moment().format('YYYY-MM-DD hh:mm:ss'),
							});
						}
					} else {
						let matched = [];
						let len = cmd.length;
						if ( settings[0] ) {
							rarities.forEach(rarity => {
								if ( rarity.slice(0, len) == cmd )
									matched.push(rarity);
							});
							types.forEach(type => {
								if ( type.slice(0, len) == cmd )
									matched.push(type);
							});
						} else {
							rarities.forEach(rarity => {
								if ( rarity == cmd )
									matched.push(rarity);
							});
							types.forEach(type => {
								if ( type == cmd )
									matched.push(type);
							});
						}
						if ( matched.length > 1 ) {
							let s = '';
							matched.forEach(str => {
								s += str + ', ';
							});
							s = s.slice(0, s.length - 2);
							log(`Abbreviation is not distinguishable(${s}).`, 'red');
						} else if ( matched.length == 0 ) {
							log(`No match for '${cmd}' in dictionary.`, 'red');
						} else {
							matched = matched[0];
							let idx = rarities.findIndex(rarity => rarity == matched);
							if ( idx != -1 ) {
								selectedRarity = idx;
								log(`Selected rarity ${matched}.`);
							} else {
								selectedType = types.findIndex(type => type == matched);
								log(`Selected type ${matched}.`);
							}
						}
					}
				} else if ( state == 4 ) {
					if ( cmd == '1' ) {
						clr();
						log("Input the rarity you want to apply to the filter.");
						log("Input '1' for any.");
						state = 5;
					} else if ( cmd == '2' ) {
						clr();
						log("Input the type you want to apply to the filter.");
						log("Input '1' for any.");
						state = 6;
					} else if ( cmd == 'q' ) {
						clr();
						let cnt0 = 0, cnt1 = 0, cost = 0;
						let latest;
						craftRecord.forEach(record => {
							if ( record.rarity == qRarity || qRarity == -1 ) {
								if ( record.type == qType || qType == -1 ) {
									latest = record;
									if ( record.state == false ) {
										cnt0 ++;
									} else {
										cnt1 ++;
									}
									cost += record.cost;
								}
							}
						});
						if ( latest === undefined ) {
							log(`There are no crafts that satisfy all the filters.`);
						} else {
							let be = 'are';
							if ( cnt1 == 1 )
								be = 'is';

							if ( qRarity != -1 && qType != -1 )
								log(`${cnt1} crafts using ${rarities[qRarity]} ${types[qType]} ${be} successful`, 'green');
							else if ( qRarity == -1 && qType == -1 ) 
								log(`${cnt1} crafts ${be} successful`, 'green');
							else if ( qRarity == -1 ) 
								log(`${cnt1} crafts using any ${types[qType]} ${be} successful`, 'green');
							else
								log(`${cnt1} crafts using ${rarities[qRarity]} petals ${be} successful`, 'green');

							be = 'are';
							if ( cnt0 == 1 )
								be = 'is';

							if ( qRarity != -1 && qType != -1 )
								log(`${cnt0} crafts using ${rarities[qRarity]} ${types[qType]} ${be} unsuccessful`, 'red');
							else if ( qRarity == -1 && qType == -1 ) 
								log(`${cnt0} crafts ${be} unsuccessful`, 'red');
							else if ( qRarity == -1 ) 
								log(`${cnt0} crafts using any ${types[qType]} ${be} unsuccessful`, 'red');
							else
								log(`${cnt0} crafts using ${rarities[qRarity]} petals ${be} unsuccessful`, 'red');

							log(`You have ${cnt1} out of ${cnt0 + cnt1} crafts successful.`);
							log(`Your success rate is ${(cnt1 / (cnt0 + cnt1) * 100).toFixed(2)}%`);
							if ( qRarity != -1 ) {
								log(`The nominal success rate is ${craftP[qRarity] * 100}%`);
								log(`You have ${(craftP[qRarity] * (cnt0 + cnt1)).toFixed(2)} crafts successful on expectations.`);
							}
							log("");
							log(`You crafted ${cnt1} petals out of ${cost} petals.`);
							log(`You spend ${(cost / cnt1).toFixed(2)} petals for each success on average.`);
							if ( qRarity != -1 ) {
								log(`You spend ${((craftP[qRarity] * 5 + (1 - craftP[qRarity]) * 2.5) / craftP[qRarity]).toFixed(2)} petals for each success on expectations.`);
							}
							log("");
							log(`Your last craft was on ${latest.date}, which was ${latest.state ? 'successful' : 'unsuccessful'}.`, latest.state ? 'green' : 'red');
							if ( qRarity != -1 && qType != -1 ) {
								let cnt = 0;
								for (let i = craftRecord.length - 1; i > 0; i -- ) {
									let record = craftRecord[i];
									if ( record.type == qType && record.rarity == qRarity) {
										if ( record.state ) {
											break;
										} else {
											cnt ++;
										}
									}
								}
								log(`You have failed crafting this petal for ${cnt} times since last success.`);
								log(`The next craft will have a ${(cnt * craftC[qRarity] * 100).toFixed(2)}% chance to success.`);
							}
						}
						log('Execute command \'help\' for more information.');
						state = 0;
					} else if ( cmd == 'exit' ) {
						log('Exited');
						log('Execute command \'help\' for more information.');
						state = 0;
					} else {
						log("Invalid input");
					}
				} else if ( state == 5 ) {
					let matched = [];
					let len = cmd.length;
					if ( settings[0] ) {
						rarities.forEach(rarity => {
							if ( rarity.slice(0, len) == cmd )
								matched.push(rarity);
						});
					} else {
						rarities.forEach(rarity => {
							if ( rarity == cmd )
								matched.push(rarity);
						});
					}
					if ( matched.length > 1 ) {
						let s = '';
						matched.forEach(str => {
							s += str + ', ';
						});
						s = s.slice(0, s.length - 2);
						log(`Abbreviation is not distinguishable(${s}).`, 'red');
					} else if ( matched.length == 0 ) {
						qRarity = -1;
						clr();
						state = 4;
						log(`Rarity filter has been changed to any.`);
						q();
					} else {
						matched = matched[0];
						let idx = rarities.findIndex(rarity => rarity == matched);
						if ( idx != -1 ) {
							qRarity = idx;
							clr();
							state = 4;
							log(`Rarity filter has been changed to ${matched}.`);
							q();
						} else { // should not happen
							log('ERROR', 'red');
						}
					}
				} else if ( state == 6 ) {
					let matched = [];
					let len = cmd.length;
					if ( settings[0] ) {
						types.forEach(type => {
							if ( type.slice(0, len) == cmd )
								matched.push(type);
						});
					} else {
						types.forEach(type => {
							if ( type == cmd )
								matched.push(type);
						});
					}
					if ( matched.length > 1 ) {
						let s = '';
						matched.forEach(str => {
							s += str + ', ';
						});
						s = s.slice(0, s.length - 2);
						log(`Abbreviation is not distinguishable(${s}).`, 'red');
					} else if ( matched.length == 0 ) {
						qType = -1;
						clr();
						state = 4;
						log(`Type filter has been changed to any.`);
						q();
					} else {
						matched = matched[0];
						let idx = types.findIndex(type => type == matched);
						if ( idx != -1 ) {
							qType = idx;
							clr();
							state = 4;
							log(`Type filter has been changed to ${matched}.`);
							q();
						} else { // should not happen
							log('ERROR', 'red');
						}
					}
					
				} else if ( state == 7 ) {
					if ( cmd == 'exit' ) {
						log('Exited');
						log('Execute command \'help\' for more information.');
						state = 0;
					} else if ( cmd == 'del' ) {
						log("THIS OPERATION CAN'T BE REVOKED", 'red');
						log('Input the ID of the log you want to delete.');
						state = 8;
					} else {
						let page = parseInt(cmd);
						if ( page === NaN || page <= 0 ) {
							log('Invalid input', 'red');
						} else {
							listPage = page;
							clr();
							list();
						}
					}
				} else if ( state == 8 ) {
					let id = parseInt(cmd);
					if ( id === NaN ) {
						log('Invalid Input', 'red');
					} else {
						craftRecord.splice(id - 1, 1);
						state = 7;
						clr();
						list();
					}
				}
			}
		}
	});
	window.addEventListener('beforeunload', () => {
		window.localStorage.setItem('betterRrolf_craft_record', JSON.stringify(craftRecord));
		window.localStorage.setItem('betterRrolf_settings', JSON.stringify(settings));
	});
}

function help() {
	log('Press [/] to toggle BetterRrolf console.');
	log('Press [.] to focus on BetterRrolf console.');
	log("Commands(Use without dot):");
	log(".help: Open this page.");
	log(".set: Open settings page.");
	log(".clr: Clear console.");
	log(".log: Start logging your crafts.");
	log(".q: Query your crafts.");
	log(".list: List your crafts(you can also operate logs here | currently unavailable).");
	// log(".acc: ");
	log("Commands will be explained more specificly when you execute them.");
}

function set() {
	state = 1;

	log("Settings:");

	log("ID: 1");
	log("abbrCheckWhenLogging(true/false):");
	if ( settings[0] )
		log("true", 'green');
	else
		log("false", 'red');
	log("When enabled, checks abbreviation for rarities and types (of petals) when logging crafts.");
	log("");

	log("ID: 2");
	log("focusEventLogging(true/false):");
	if ( settings[1] )
		log("true", 'green');
	else
		log("false", 'red');
	log("When enabled, focus events will be logged in console.");
	log("");

	log("Select the ID of the option you want to change or 0 to exit");
}

function log(str, clr) {
	if ( clr === undefined ) {
		clr = 'default';
	}
	console.log('%c' + consoleLogPrefix + str, `background: #E3F8FF; color: ${color[clr]}`);
}

function q() {
	let len = craftRecord.length;
	if ( len == 0 ) {
		log('There are no crafting records yet.');
		log('Execute command \'help\' for more information.');
		state = 0;
	} else {
		log("Current filters:");
		log("rarity:");
		if ( qRarity == -1 ) {
			log("any");
		} else {
			log(rarities[qRarity]);
		}
		log("type:");
		if ( qType == -1 ) {
			log("any");
		} else {
			log(types[qType]);
		}
		log("");
		log("Input '1' to change rarity filter.");
		log("Input '2' to change type filter.");
		log("Input 'q' again to query.");
		log("Input 'exit' to exit. ");
	}
}

function list() {
	let len = craftRecord.length;
	if ( len == 0 ) {
		log('There are no crafting records yet.');
		log('Execute command \'help\' for more information.');
		state = 0;
	} else {
		log("Input a page number to turn to that page.");
		log("Input 'del' to delete a record.");
		log("Input 'exit' to exit.");
		let pageCount = Math.floor((len - 1) / pageLength) + 1;
		log(`Page ${listPage} / ${pageCount}`);
		for (let i = (listPage - 1) * pageLength + 1; i <= listPage * pageLength && i <= len; i ++ ) {
			let record = craftRecord[i - 1];
			log(`${i} | Attempt to craft ${record.rarity} ${record.type} on ${record.date} ${record.state ? "succeeded" : "failed and lost " + record.cost }`, record.state ? 'green' : 'red');
		}
	}
}

function initInputBox() {
	let newInputBox = document.createElement('input');
	newInputBox.id = `betterRrolfConsole`;
	newInputBox.type = 'text';
	newInputBox.placeholder = `Better Rrolf Console`;
	document.body.append(newInputBox);
	let style = document.getElementById('betterRrolfConsole').style;
	style['height'] = '3vh';
	style['width'] = '27vw';
	style['position'] = 'absolute';
	style['z-index'] = '4';
	style['left'] = '85vw';
	style['top'] = '3vh';
	style['transform'] = 'translateY(-50%) translateX(-50%)';
	style['padding'] = '0vh';
	style['background-color'] = 'rgba(255, 255, 255, 0.7)';
	style['outline-color'] = 'rgba(0, 0, 0, 0)';
	style['outline-style'] = 'solid';
	style['outline-width'] = '0vh';
	style['border-radius'] = '0.5vh';
	style['font-size'] = '2.5vh';
	style['font-family'] = 'Ubuntu';
	input = document.getElementById('betterRrolfConsole');
}

function initDependencies() {
	let script = document.createElement('script');
	script.src = 'https://momentjs.com/downloads/moment.min.js';
	script.async = true;
	document.head.append(script);
}

function toggleInputBox() {
	if ( enabled ) {
		enabled = false;
		document.getElementById('betterRrolfConsole').style.visibility = 'hidden';
	} else {
		enabled = true;
		document.getElementById('be·tterRrolfConsole').style.visibility = 'visible';
	}
}

function clr() {
	console.clear();
}