HWHhuntFragmentExt

Extension for HeroWarsHelper script

// ==UserScript==
// @name			HWHhuntFragmentExt
// @name:en			HWHhuntFragmentExt
// @name:ru			HWHhuntFragmentExt
// @namespace		HWHhuntFragmentExt
// @version			0.0.49
// @description		Extension for HeroWarsHelper script
// @description:en	Extension for HeroWarsHelper script
// @description:ru	Расширение для скрипта HeroWarsHelper
// @author			dimaka1256
// @license 		Copyright dimaka1256
// @homepage		none
// @icon			https://zingery.ru/scripts/VaultBoyIco16.ico
// @icon64			https://zingery.ru/scripts/VaultBoyIco64.png
// @match			https://www.hero-wars.com/*
// @match			https://apps-1701433570146040.apps.fbsbx.com/*
// @run-at			document-start
// ==/UserScript==

(function () {

    if (!this.HWHClasses) {
		console.log('%cObject for extension not found', 'color: red');
		return;
	}

	console.log('%cStart Extension ' + GM_info.script.name + ', v' + GM_info.script.version + ' by ' + GM_info.script.author, 'color: red');
	const { addExtentionName } = HWHFuncs;
	addExtentionName(GM_info.script.name, GM_info.script.version, GM_info.script.author);

	const {
		setProgress,
		getSaveVal,
		setSaveVal,
		popup,
        I18N,
		getUserInfo,
	} = HWHFuncs;

let {buttons,i18nLangData} = HWHData;

let ruLang ={
    FRAGMENT_HUNT: 'Слить энку',
    FRAGMENT_HUNT_TITLE: 'Добывать фрагменты шмоток/рецептов',
	FRAGMENT_HUNT_SETUP: '⚙️',
    FRAGMENT_HUNT_SETUP_TITLE: 'Выбор фрагмента/миссии',
	FRAGMENT_HUNT_ENERGY: '⚡️',
    FRAGMENT_HUNT_FR: '🧩',
    FRAGMENT_HUNT_MISSION: 'миссия',
	FRAGMENT_HUNT_WORLD: 'Глава',
	FRAGMENT_HUNT_SPENT: 'Потратили ',
    FRAGMENT_HUNT_GOTFRAGMENTS: ', получили такой лут:',
    FRAGMENT_HUNT_PCS: 'шт',
    FRAGMENT_HUNT_WEHUNT: 'Добываем предметы',
    FRAGMENT_HUNT_ENOUGH1: ', хватит на ',
    FRAGMENT_HUNT_ENOUGH2: ' рейдов x10',
    FRAGMENT_HUNT_CHANGE: 'Выбрать',
    FRAGMENT_HUNT_PARTS1: 'Фиол шмот',
    FRAGMENT_HUNT_PARTS2: 'Фиол свитки А-О',
    FRAGMENT_HUNT_PARTS3: 'Фиол свитки П-Я',
    FRAGMENT_HUNT_PARTS4: 'Жёлтый шмот',
    FRAGMENT_HUNT_PARTS5: 'Жёлтые свитки А-К',
    FRAGMENT_HUNT_PARTS6: 'Жёлтые свитки Л-Я',
    FRAGMENT_HUNT_PARTS7: 'Красный шмот',
    FRAGMENT_HUNT_PARTS8: 'Красные свитки',
    FRAGMENT_HUNT_CHOOSEPART: 'Какую категорию шмотки ищём?',
    FRAGMENT_HUNT_CHOOSEITEM: 'Какую шмотку ищём?',
    FRAGMENT_HUNT_CHOOSEMISSION: 'В какой миссии? (см. ещё дроп)',
	FRAGMENT_HUNT_NOTENOUGH: 'Недостаточно энергии',
	FRAGMENT_HUNT_NOMISSION: 'Не выбрана миссия, нечего добывать',
	FRAGMENT_HUNT_DOALL: 'Слить энку на миссию',
	FRAGMENT_HUNT_NOVIP: 'Сорри, без VIP1 не работает',
	FRAGMENT_HUNT_SMALLVIP: 'Без VIP5 бьёт одиночными рейдами',
}
let enLang ={
    FRAGMENT_HUNT: 'Spend Stamina',
    FRAGMENT_HUNT_TITLE: 'Hunt for Item/Scroll fragment',
	FRAGMENT_HUNT_SETUP: '⚙️',
    FRAGMENT_HUNT_SETUP_TITLE: 'Choose fragment/mission',
	FRAGMENT_HUNT_ENERGY: '⚡️',
	FRAGMENT_HUNT_SPENT: 'Spent ',
    FRAGMENT_HUNT_FR: '🧩',
    FRAGMENT_HUNT_MISSION: 'mission',
	FRAGMENT_HUNT_WORLD: 'World',
    FRAGMENT_HUNT_GOTFRAGMENTS: ',g ot this loot:',
    FRAGMENT_HUNT_PCS: 'pcs',
    FRAGMENT_HUNT_WEHUNT: 'We hunt for items',
    FRAGMENT_HUNT_ENOUGH1: ', enough for ',
    FRAGMENT_HUNT_ENOUGH2: ' raids x10',
    FRAGMENT_HUNT_CHANGE: 'Choose',
    FRAGMENT_HUNT_PARTS1: 'Purple gear',
    FRAGMENT_HUNT_PARTS2: 'Purple scrolls 1',
    FRAGMENT_HUNT_PARTS3: 'Purple scrolls 2',
    FRAGMENT_HUNT_PARTS4: 'Yellow gear',
    FRAGMENT_HUNT_PARTS5: 'Yellow scrolls 1',
    FRAGMENT_HUNT_PARTS6: 'Yellow scrolls 2',
    FRAGMENT_HUNT_PARTS7: 'Red gear',
    FRAGMENT_HUNT_PARTS8: 'Red scrolls',
    FRAGMENT_HUNT_CHOOSEPART: 'Choose fragment category:',
    FRAGMENT_HUNT_CHOOSEITEM: 'Choose fragment:',
    FRAGMENT_HUNT_CHOOSEMISSION: 'Choose mission(look at other drop)',
	FRAGMENT_HUNT_NOTENOUGH: 'Not enough stamina',
	FRAGMENT_HUNT_NOMISSION: 'Mission not chosen',
	FRAGMENT_HUNT_DOALL: 'Consume stamina to a mission',
	FRAGMENT_HUNT_NOVIP: 'This requires at least VIP1 to run',
	FRAGMENT_HUNT_SMALLVIP: 'Only single raids without VIP5',
}
Object.assign(i18nLangData.ru, ruLang)
Object.assign(i18nLangData.en, enLang)
this.HWHData.i18nLangData = i18nLangData;

const fragmentHuntButton = {
    fragmentHuntButton: {
		isCombine: true,
		combineList: [
			{
				get name() { return I18N('FRAGMENT_HUNT'); }, 
				get title() { return I18N('FRAGMENT_HUNT_TITLE'); },
        		onClick: gohuntFragment,
				hide: false,
				color: 'red'
			},
			{
        		get name() { return I18N('FRAGMENT_HUNT_SETUP'); }, 
				get title() { return I18N('FRAGMENT_HUNT_SETUP_TITLE'); },
        		onClick: setuphuntFragment,
				hide: false,
				color: 'red'
			},
		]
}}

Object.assign(buttons,fragmentHuntButton)
this.HWHData.buttons = buttons;


function setuphuntFragment() {
		let fragment= new huntFragment();
		fragment.setup();
}

function gohuntFragment() {
		let fragment= new huntFragment();
		fragment.start();
}

//добавить галочку в "сделать всё"
//я буду гореть за это в аду
const task = {
			name: 'gohuntFragment',
			label: I18N('FRAGMENT_HUNT_DOALL'),
			checked: false
		}
const functions2 = {
		gohuntFragment
}

const {doYourBest} = HWHClasses;
const doIt = new doYourBest();
	let myfuncList=doIt.funcList
	myfuncList.splice (-3,0,task);
	let myfunctions = doIt.functions
	Object.assign(myfunctions, functions2)

	class extdoYourBest extends doYourBest {
		funcList = myfuncList
		functions = myfunctions
	}
this.HWHClasses.doYourBest = extdoYourBest;


class huntFragment {

	inventoryGet = []
	droptable = []
	stamina = 0
    raids = 0
    missionID = 0
    energyNeeded = 0

missionEnergy (id) {
		if (id == 0) {return 999999};
        if (id > 145) {return 10};
	    if (id  < 86) {return 6};
		return 8
}

checkvip() {
	let currentVipPoints = (getUserInfo()).vipPoints;
	if (currentVipPoints >999) {return 5}
	if (currentVipPoints >9) {return 1}
	return 0
}

isWithinRange(value, min, max) {
  return value >= min && value <= max;
}

generateArray(start, size) {
  return Array.from({length: size}, (_, index) => index + start);
}

getType(id){
	let type = "oops"
	if (this.isWithinRange (id, 21, 55) || this.isWithinRange (id, 56, 99) || this.isWithinRange (id, 167, 178) || this.isWithinRange (id, 221, 232)) {type="Gear"}
	if (this.isWithinRange (id, 141, 166) || this.isWithinRange (id, 190, 220) || this.isWithinRange (id, 244, 254)) {type="Scroll"}
	return type
}

getColor(id){
    if (this.isWithinRange (id, 21, 55)) {return "green"}
    if (this.isWithinRange (id, 141, 145)) {return "green"}
    if (this.isWithinRange (id, 56, 90)) {return "blue"}
	if (this.isWithinRange (id, 91, 166)) {return "purple"}
	if (this.isWithinRange (id, 167, 220)) {return "orange"}
	if (this.isWithinRange (id, 221, 254)) {return "red"}
	console.log ("getColor oops", id); return "white"
}

getName(id){
	this.updatemyData()
	let itemAvailable = 0
	let fullitemAvailable = 0;
	switch (this.getType(id)) {
	 case "Gear": {
		itemAvailable = this.inventoryGet.fragmentGear[id] ?? 0;
		fullitemAvailable = this.inventoryGet.gear[id] ?? 0; break;
	}
	 case "Scroll": {
		itemAvailable = this.inventoryGet.fragmentScroll[id] ?? 0;
		fullitemAvailable = this.inventoryGet.scroll[id] ?? 0; break;
	}
	}

	let color = this.getColor(id)
	let name = cheats.translate(`LIB_${(this.getType(id)).toUpperCase()}_NAME_${id}`);
	let words = name.split(" ")
	for (let i in words) {if (words[i].length > 8){name=name.replace(words[i],(words[i]).substring(0,5)+".")}}
	name = name.replace(" - Рецепт","-р"); 
	name = name.replace(", уровень ","-"); 
	name = name.replace(" уровень ","-");//эти сокращения локалить впадлу, совсем
	let out =""
    //out+=id //если нужен ид шмотки
	out+=' <span style="color:'+color+';">'+name+'</span> ('+fullitemAvailable+'+'+itemAvailable+I18N('FRAGMENT_HUNT_FR')+')';
	return out
}

generateitembuttons(itemids) {
	const buttons = [];
	for (let itemid in itemids) {
		let gearID = itemids[itemid]
		let name = this.getName(gearID);
		buttons.push({
					msg:  name,
					result: itemids[itemid],
					get title() { return name },
				});
	}
	buttons.push({msg: I18N('BTN_CANCEL'), result: false, isCancel: true})
	return buttons
}

generatemissionbuttons(missions, itemid) {
	const buttons = [];
	for (let mission in missions) {
		let missionID = missions[mission]
		let thismission = this.droptable.filter(m=>m.id===missionID)[0]
		let otheritems = thismission.drop.filter(d=>d != itemid) 
		let name = I18N('FRAGMENT_HUNT_WORLD')+" "+thismission.world+" "+I18N('FRAGMENT_HUNT_MISSION')+" "+thismission.index+"   "+this.missionEnergy(thismission.id)+I18N('FRAGMENT_HUNT_ENERGY')+"<br>"
		// I18N('FRAGMENT_HUNT_MISSION')+" "+ thismission.id
		for(let i in otheritems) {if (this.getType(otheritems[i]) === "oops"){continue;};name+=this.getName(otheritems[i])+"<br>";} 
		buttons.push({
					msg:  name,
					result: missionID,
					get title() { return name },
				});
	}
	buttons.push({msg: I18N('BTN_CANCEL'), result: false, isCancel: true})
	return buttons
}

async updatemyData(){
		let calls = [{
			name: "userGetInfo",
			args: {},
			ident: "userGetInfo"
		},{
			name: "inventoryGet",
			args: {},
			ident: "inventoryGet"
		}];
		let result = await Send(JSON.stringify({ calls }));
		let infos = result.results;
		this.stamina = (infos[0].result.response.refillable.find(n => n.id == 1)).amount;
		this.inventoryGet = infos[1].result.response;

	    this.energyNeeded = this.missionEnergy(this.missionID)
        //if (this.missionID > 145) {this.energyNeeded = 10};
	    //if (this.missionID  < 86) {this.energyNeeded = 6};
		//console.log ("vip",this.checkvip())
        switch (this.checkvip()){
			case 5: {this.raids = Math.floor(this.stamina/(this.energyNeeded*10)); break;}
			case 1: {this.raids = Math.floor(this.stamina/(this.energyNeeded)); break}
			case 0: {this.raids = 0; setProgress(I18N('FRAGMENT_HUNT_NOVIP'))}
		}
		return "ok"
}

async makeMission(mission,count){
	let nrg = this.energyNeeded*count;
	let calls = []

			switch (this.checkvip()){
			case 5: {calls.push({name: "missionRaid",args: {id: mission.id,times: (count)},ident: "body"}); break;}
			case 1: {
					for (let i = 0; i < count; i++)
					{
						calls.push({name: "missionRaid",args: {id: mission.id,times: 1},ident: "body"+"i"})
					}
					
				}
			}
			let result = await Send({ calls })
            console.log(result)
			let loot2 = []
			for (let j in result.results)
			{
				let loot = result.results[j].result.response
				for (let i in loot){
					if (!!loot[i].fragmentScroll) {loot2.push(loot[i].fragmentScroll)}
					if (!!loot[i].fragmentGear) {loot2.push(loot[i].fragmentGear)}
				}
			}
				let loot3=[]
                //console.log("loot2",loot2)
				for (let i in loot2) {
                    for (let j in Object.keys(loot2[i])) {loot3.push(Object.keys(loot2[i])[j])}                    
			}
			await this.updatemyData()
            //console.log("loot3",loot3)
			let str = I18N('FRAGMENT_HUNT_SPENT')+nrg+I18N('FRAGMENT_HUNT_ENERGY')+ I18N('FRAGMENT_HUNT_GOTFRAGMENTS')+"<br>"
			let usedids = []
			let loot4=[]
			for (let i in loot3) {
				if (!usedids.includes(loot3[i])){loot4.push({id:loot3[i],qty:1}); usedids.push(loot3[i])}
				else {(loot4.find(e => e.id== loot3[i])).qty++}
			}
			for (let i in loot4){
				str+=this.getName(loot4[i].id)+": "+loot4[i].qty+ I18N('FRAGMENT_HUNT_PCS')+"<br>"
			}
			setProgress(str)
            //if (this.raids >0) {this.start(false)}
}

updateDroptable(){
  const dropTable2 = ((types = ['gear', 'fragmentGear', 'scroll', 'fragmentScroll']) => {
  return Object.values(lib.data.mission).map(e => {
  const lastWave = e.normalMode?.waves?.at(-1);
  const lastEnemy = lastWave?.enemies?.at(-1);
  let dropList = lastEnemy?.drop ?? [];
  let heromissions = []
  for(const i of dropList) {
	const type = Object.keys(i.reward)[0]
	if (type == 'fragmentHero') {
		//console.log('heromission',e.id); 
		heromissions.push(e.id)
	}
  }
  const drop = [];
  for(const d of dropList) {
    const type = Object.keys(d.reward).pop()
    if (d.chance && types.includes(type) && !(heromissions.includes(e.id))) {
      const id = Object.keys(d.reward[type]).pop()
      if (id>90) {drop.push(+id)}
    }
  }
  return {id: e.id, world: e.world, index: e.index, drop}
}).filter(n => n.drop.length)
})()
//console.log("dropTable2",dropTable2)
this.droptable = dropTable2
}

async start() {
	this.updateDroptable()
	this.missionID = getSaveVal('huntFragmentMission', 999)
	let mission = this.droptable.filter(m=>m.id===this.missionID)[0]
	//console.log ("start missionID",this.missionID,mission)
    if (!mission) {console.log("nomission"); this.setup();} //если в конфиге кака сначала настройка
	await this.updatemyData()
		console.log("Энки у нас",this.stamina," рейдов доступно",this.raids)

		if (this.raids >0) {
			 if (this.checkvip() == 5){this.makeMission(mission,this.raids*10); }
			 else {this.makeMission(mission,this.raids); }
		} 
		else {setProgress(I18N('FRAGMENT_HUNT_NOTENOUGH'))}
	}

async setup() {
	this.updateDroptable()
	//console.log("droptable2 updated",this.droptable)
	this.missionID = getSaveVal('huntFragmentMission', 999)
	let mission = this.droptable.filter(m=>m.id===this.missionID)[0]
	//console.log ("setup missionID",this.missionID)
	let message = ""; let maxraid =""; let target="";
	if (this.checkvip() != 5) {message+=I18N('FRAGMENT_HUNT_SMALLVIP')}

	if (!mission) {
		this.raids = 0; 
		message = I18N('FRAGMENT_HUNT_NOMISSION')} //костыль чтоб точно выбрали миссию
	else {
		await this.updatemyData()
		for (let i in mission.drop){let id=mission.drop[i]; target+=this.getName(id)+"<br>";}
		maxraid = I18N('RAID')+" х"
		if (this.checkvip()==5) {maxraid+=this.raids*10} else {maxraid+=this.raids}
		message = I18N('FRAGMENT_HUNT_WEHUNT')+":<br> "+target+" <br>"+I18N('FRAGMENT_HUNT_WORLD')+" "+mission.world+" "+I18N('FRAGMENT_HUNT_MISSION')+" "+mission.index+"    "+this.energyNeeded+" "+I18N('FRAGMENT_HUNT_ENERGY')+"<br>"+I18N('FRAGMENT_HUNT_ENERGY')+" "+this.stamina+I18N('FRAGMENT_HUNT_ENOUGH1')+this.raids+I18N('FRAGMENT_HUNT_ENOUGH2')
	}
	
	let buttons0 = []
	if (this.raids > 0 && this.checkvip()==5) {buttons0.push({msg: I18N('RAID')+" х10", result: "1"})}
	if (this.raids > 1) {buttons0.push({msg: maxraid, result: "2"})}
	buttons0.push({msg: I18N('FRAGMENT_HUNT_CHANGE'), result: "3"})
	buttons0.push({msg: I18N('BTN_CANCEL'), result: false})

	this.answerr = await popup.confirm(message, buttons0);
	
	const parts = [I18N('FRAGMENT_HUNT_PARTS1'),I18N('FRAGMENT_HUNT_PARTS2'),I18N('FRAGMENT_HUNT_PARTS3'),I18N('FRAGMENT_HUNT_PARTS4'),I18N('FRAGMENT_HUNT_PARTS5'),I18N('FRAGMENT_HUNT_PARTS6'),I18N('FRAGMENT_HUNT_PARTS7'),I18N('FRAGMENT_HUNT_PARTS8')]
	const buttons = [];
	for (let i in parts ) {
		buttons.push({
					msg: parts[i],
					result: i,
					get title() { return "test" },
				});
	}
	buttons.push({msg: I18N('BTN_CANCEL'), result: false, isCancel: true})
	let answer=0;
	switch (this.answerr) { 
		case "1": {
			//console.log("тут будет рейд х10",mission.id);
			this.makeMission(mission,10)
			break;
			}
		case "2": {
			//console.log("тут будет макс рейд",mission.id); 
			this.makeMission(mission,this.raids*10)
			break;}
		case "3": {answer = await popup.confirm(I18N('FRAGMENT_HUNT_CHOOSEPART'), buttons); break;}
		default: {return;}
	}
	if (!answer) {return}

	let array=[]
	switch (answer) { 
		case "0": { array=this.generateArray(91,8);break;}
		case "1": { array=[158,153,162,164,165,157,152,166]; break;} //(152,15)
		case "2": { array=[163,159,154,161,156,160,155]; break;}
		case "3": { array=this.generateArray(167,12);break;}
		case "4": { array=[190,194,197,205,204,215,214,196,218,193,219,217,206]; break;}
		case "5": { array=[198,220,195,192,216,191,199,200]; break;}
		case "6": { array=this.generateArray(221,12); break;}
		case "7": { array=this.generateArray(244,11); break;}
		default: {console.log("oops"); break;}
	}
	let answer2 = await popup.confirm(I18N('FRAGMENT_HUNT_CHOOSEITEM'), this.generateitembuttons(array));
	if (!answer2) {return}

	let missions = this.droptable.filter(m=>m.drop.includes(answer2))
	let missarray = []
	for (let i in missions) {missarray.push(missions[i].id)}
	let answer3 = await popup.confirm(I18N('FRAGMENT_HUNT_CHOOSEMISSION')+"<br>"+this.getName(answer2), this.generatemissionbuttons(missarray,answer2));
	if (!answer3) {return}

	setSaveVal('huntFragmentMission', answer3)
	this.setup()
}
}
this.HWHClasses.huntFragment = huntFragment;
})();

//TODO:
// формат "одной кнопки" для "сделать всё"