您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A user script for The West, optimized Dobby2 for butt plugs and cake decorations.
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/512872/1465973/Janka%20Tietzova.js
// ==UserScript== // @name Janka Tietzova // @namespace http://tampermonkey.net/ // @version 2.1.1 // @description A user script for The West, optimized Dobby2 for butt plugs and cake decorations. // @author Chvostnatý Gábor // @include https://*.the-west.*/game.php* // @license MIT // @icon data:image/jpeg;base64,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 // @grant none // @downloadURL https://update.gf.qytechs.cn/scripts/497915/Zdenka%20Studenkova.user.js // @updateURL https://update.gf.qytechs.cn/scripts/497915/Zdenka%20Studenkova.meta.js // ==/UserScript== (function() { async function sleep(milliseconds) { await new Promise(r => setTimeout(r, milliseconds)) } function parseConsumableBonuses(bonuses) { const { energyText, motivationText, healthText, speedText, luckText, dropText, experienceText, moneyText } = Vajda.searchKeys[Vajda.language] const result = { energy: 0, health: 0, motivation: 0, hasBuffs: false, buffs: { character: { luck: 0, money: 0, experience: 0, drop: 0 }, travel: 0 } } function getValue(bonus, bonusText) { return Number(bonus.toLowerCase().replace(bonusText.toLowerCase(), '').replace(' ', '').replace('%', '').replace('+', '')) } for (const bonus of bonuses) { if ( bonus.includes(energyText) ) { result.energy = getValue(bonus, energyText) continue } if ( bonus.includes(motivationText) ) { result.motivation = getValue(bonus, motivationText) continue } if ( bonus.includes(healthText) ) { result.health = getValue(bonus, healthText) continue } if ( bonus.includes(speedText) ) { result.buffs.travel = getValue(bonus, speedText) result.hasBuffs = true continue } if ( bonus.includes(luckText) ) { result.buffs.character.luck = getValue(bonus, luckText) result.hasBuffs = true continue } if ( bonus.includes(experienceText) ) { result.buffs.character.experience = getValue(bonus, experienceText) result.hasBuffs = true continue } if ( bonus.includes(moneyText) ) { result.buffs.character.money = getValue(bonus, moneyText) result.hasBuffs = true continue } if ( bonus.includes(dropText) ) { result.buffs.character.drop = getValue(bonus, dropText) result.hasBuffs = true } } return result } class Job { constructor(x, y, id, groupId) { this.x = x this.y = y this.id = id this.groupId = groupId this.isSilver = false this.distances = [] this.experience = 0 this.money = 0 this.motivation = 0 this.stopMotivation = 75 this.set = -1 this.bestEquipment_ = undefined } setIsSilver(val) { this.isSilver = val return this } setExperience(val) { this.experience = val return this } setMoney(val) { this.money = val return this } setMotivation(valOrUpdater) { if ( typeof valOrUpdater === 'function' ) { this.motivation = valOrUpdater(this.motivation) } else { this.motivation = valOrUpdater } this.motivation = Math.min(100, this.motivation) return this } setStopMotivation(val) { this.stopMotivation = val return this } setSet(index) { this.set = index return this } getDistances() { return this.distances } async loadMotivation() { await Ajax.get('job', 'job', {jobId: this.id, x: this.x, y: this.y}, (r) => { this.motivation = r.motivation * 100 }) return this.motivation } /** * Calculate job distance to current character position */ calculateDistance() { this.distance = GameMap.calcWayTime({x: this.x, y: this.y}, Character.position) return this.distance } /** * Calculate job distance to other selected jobs */ calculateJobDistances() { this.distances = [] for ( const job of Vajda.addedJobs ) { this.distances.push(GameMap.calcWayTime({x: this.x, y: this.y}, {x: job.x, y: job.y})) } return this.distances } saveBestEquipment() { const skills = JobsModel.Jobs.find(j => j.id === this.id).get('skills') const set = west.item.Calculator.getBestSet(skills, this.id) const itemIds = set && set.getItems() || [] this.bestEquipment_ = itemIds return itemIds } get bestEquipment() { return this.bestEquipment_ === undefined ? this.saveBestEquipment() : this.bestEquipment_ } getBestEquipment() { if (!Bag.loaded) { EventHandler.listen('inventory_loaded', function() { this.getBestEquipment() return EventHandler.ONE_TIME_EVENT }) return } const items = Bag.getItemsByItemIds(this.bestEquipment) const result = [] for (const item of items) { const wearItem = Wear.get(item.getType()) if (!wearItem || (wearItem && (wearItem.getItemBaseId() !== item.getItemBaseId() || wearItem.getItemLevel() < item.getItemLevel()))) { result.push(item) } } return result.map(item => item.obj.getId()) } } class Consumable { constructor(id, image, name) { this.id = id this.image = image this.name = name this.energy = 0 this.motivation = 0 this.health = 0 this.isSelected_ = false this.count = 0 } isCakeDecoration() { return this.id === 53339000 } removeOne() { this.count -= 1 if ( this.count === 0 ) { const consumables = Manager.consumables const index = consumables.findIndex(c => c.id === this.id) consumables.splice(index, 1) } return this } setCount(valOrUpdater) { if ( typeof valOrUpdater === 'function' ) { this.count = valOrUpdater(this.count) } else { this.count = valOrUpdater } this.count = Math.max(0, this.count) return this } setEnergy(val) { this.energy = val return this } setMotivation(val) { this.motivation = val return this } setHealth(val) { this.health = val return this } setIsSelected(isSelected) { this.isSelected_ = isSelected return this } hasCooldown() { return this.energy > 0 || this.motivation > 0 || this.health > 0 } getBuffHTML() { return '<strong>No Buffs</strong>' } get isSelected() { return this.isSelected_ } } class Buff extends Consumable { constructor(id, image, name, buffs) { super(id, image, name, buffs) this.buffs_ = buffs } getBuffImage() { return `https://westsk.innogamescdn.com/images/buffs/${this.getBuffType()}.jpg` } getBuffType() { return this.buffs_.travel === 0 ? 'character' : 'travel' } canUseAsBuff() { return CharacterSkills.buffs[this.getBuffType()] === null && !this.hasCooldown() } getBuffHTML() { const containerStyle = `' display: grid; grid-template-columns: 30px auto; '` const imgContainerStyle = `' display: flex; padding-bottom: 5px; '` const buffsContainerStyle = `' display: grid; grid-template-rows: repeat(auto-fill, 15px); transform: translateY(5px) '` const buffs = this.buffs.reduce((acc, b) => acc.concat(`<p>${b}</p>`), '') const html = ` <div style=${containerStyle}> <span style=${imgContainerStyle}> <img style=align-self:end width=25px height=25px src=${this.getBuffImage()} alt='Buff Image'> </span> <span style=${buffsContainerStyle}> ${ buffs } </span> </div> ` return html } removeOne() { this.count -= 1 if ( this.count === 0 ) { const consumables = Manager.consumables const buffs = Manager.buffs let index = consumables.findIndex(c => c.id === this.id) if ( index !== -1 ) { consumables.splice(index, 1) return this } index = buffs.findIndex(b => b.id === this.id) if ( index !== -1 ) { buffs.splice(index, 1) if ( Manager.selectedBuffs.some(b => b.id === this.id) ) { Manager.selectedBuffs_[this.getBuffType()] = null } } } return this } get isSelected() { return Manager.selectedBuffs.some(b => b.id === this.id) || this.isSelected_ } get buffs() { if ( this.getBuffType() === 'travel' ) { return [`+${this.buffs_.travel}% speed`] } const buffs = [] for ( const buff in this.buffs_.character ) { if ( this.buffs_.character[buff] > 0 ) { buffs.push(`+${this.buffs_.character[buff]}% ${buff}`) } } return buffs } } class ConsumablesManager { selectedBuffs_ = { travel: null, character: null } constructor() { this.consumables_ = [] this.buffs_ = [] this.isOptimized_ = false this.jobsLeftInRound_ = 0 this.schedule_ = [] this.selectedBuffs_ = { travel: null, character: null } } async loadJobMotivation(updatedJobsMotivation = undefined) { let expectedJobCount = 0 const uniqueJobsCount = Vajda.addedJobs.length if ( updatedJobsMotivation !== undefined ) { expedtedJobCount = updatedJobsMotivation.reduce((acc, curr) => acc + curr, 0) return { expectedJobCount, uniqueJobsCount } } for ( const job of Vajda.addedJobs ) { expectedJobCount += Math.max(await job.loadMotivation() - job.stopMotivation, 0) await sleep(500) } return { expectedJobCount, uniqueJobsCount } } async createSchedule(updatedJobsMotivation) { const bottlePlugs = this.consumables.find(c => c.id === 52871000) //in case the game runs in the background and the job to travel to is not canceled we gonna need extra energy point //this is unlikely to happen but the energy wont go to waste anyway so why the fuck not const { expectedJobCount, uniqueJobsCount } = await this.loadJobMotivation(updatedJobsMotivation) this.jobsLeftInRound = expectedJobCount const availableEnergy = Character.energy const energyToFill = expectedJobCount + uniqueJobsCount - availableEnergy const refillCount = Math.ceil( energyToFill / ( bottlePlugs.energy / 100 * Character.maxEnergy ) ) //use plugs when n jobs left in round const schedule = [] for ( let i = 0; i < refillCount; i++ ) { //40 15s jobs in consumable cooldown schedule.push(40 + i*40) } this.schedule_ = schedule return schedule } isScheduledRefill() { return this.isOptimized_ && this.refillsLeft > 0 && this.jobsLeftInRound <= this.schedule.at(-1) } async checkSchedule(jobCount) { const bottlePlug = this.consumables.find(c => c.id === 52871000) for ( let i = 0; i < jobCount; i++ ) { this.jobsLeftInRound = p => p - 1 if ( this.isScheduledRefill() ) { //the use of consumable blocks the start of new jobs for a short amount of time, so this sleep exists to prevent that await sleep(10000) this.useConsumableOrWaitForCooldown(bottlePlug) this.schedule.pop() this.jobsLeftInRound = p => p - (jobCount - i - 1) return } } if ( Character.energy - jobCount <= 10 ) { //same here await sleep(10000) this.useConsumableOrWaitForCooldown(bottlePlug) this.schedule.pop() } } canUseConsumable(consumable) { if ( consumable instanceof Buff ) { return consumable.canUseAsBuff() } if ( BuffList.cooldowns[consumable.id] !== undefined && BuffList.cooldowns[consumable.id].time > new ServerDate().getTime() ) { return false } return true } findProperConsumable(motivationMissing, energyMissing, averageMotivationMissing) { const consumablesPool = this.getSelectedConsumables() function betterEnergy(item1, item2) { let distanceItem1 = Math.abs(energyMissing - item1.energy) let distanceItem2 = Math.abs(energyMissing - item2.energy) return (distanceItem1 < distanceItem2 ) ? -1 : (distanceItem1 > distanceItem2) ? 1 : 0 } function betterMotivation(item1, item2) { let distanceItem1 = Math.abs(averageMotivationMissing - item1.motivation) let distanceItem2 = Math.abs(averageMotivationMissing - item2.motivation) return (distanceItem2 < distanceItem1) ? item2 : item1 } function findMotivationConsume(consumes) { let consumeToChoose = null for ( let i = 0; i < consumes.length; i++ ) { if ( consumeToChoose === null && consumes[i].motivation !== 0) { consumeToChoose = consumes[i] continue } if ( consumeToChoose !== null && consumes[i].motivation !== 0) { consumeToChoose = betterMotivation(consumeToChoose, consumables[i]) } } return consumeToChoose } function findHealthConsume(consumes) { for ( let i = 0; i < consumes.length; i++ ) { if ( consumes[i].health !== 0 ) { return consumes[i] } } return null } if ( consumablesPool.length === 0 ) return null const consumables = consumablesPool.sort(betterEnergy) if ( Vajda.settings.addEnergy && energyMissing === 100 ) { return consumables[0] } if ( Vajda.settings.addMotivation && motivationMissing === Vajda.addedJobs.length ) { return findMotivationConsume(consumables) } if ( Vajda.settings.addHealth && Vajda.isHealthBelowLimit() ) { if ( this.isOptimized ) { this.schedule.pop() } return findHealthConsume(consumables) } } async useConsumable(consumable) { const item = Bag.getItemByItemId(consumable.id) item.showCooldown() if ( Vajda.shouldEquipHealthSet(consumable) ) await Vajda.equipSet(Vajda.healthSet) ItemUse.doIt(consumable.id) consumable.removeOne() while(true) { if ( !this.canUseConsumable(consumable) ) { $('.tw2gui_dialog_framefix').remove() break } await sleep(1) } Vajda.currentState = 1 } async useConsumableOrWaitForCooldown(consumableOrId, isSync = false) { const consumable = consumableOrId instanceof Consumable ? consumableOrId : this.getSelectedConsumables().find(c => c.id === consumableOrId) if ( consumable === undefined ) { return false } Vajda.currentState = 2 while (true) { if ( consumable.hasCooldown() === false || this.canUseConsumable(consumable) ) { break } if ( !Vajda.isRunning ) { break } await sleep(1000) } await this.useConsumable(consumable) isSync && Vajda.run() } isConsumableAdded (item) { if ( item === undefined ) return true for ( const consumable of this.consumables_ ) { if ( consumable.id === item.obj.item_id ) { return true } } for ( const buff of this.buffs_ ) { if ( buff.id === item.obj.item_id ) { return true } } return false } useBuff(type) { const buff = this.selectedBuffs_[type] if ( buff?.canUseAsBuff() ) { this.useConsumable(buff) } } addNewConsumable(item) { if ( this.isConsumableAdded(item) ) { return } const { energy, motivation, health, hasBuffs, buffs } = parseConsumableBonuses(item.obj.usebonus) if ( health === 0 && motivation === 0 && energy === 0 && !hasBuffs ) return if ( hasBuffs ) { const buff = new Buff(item.obj.item_id, item.obj.image, item.obj.name, buffs) .setEnergy(energy) .setMotivation(motivation) .setHealth(health) .setCount(item.count) return buff.hasCooldown() ? this.consumables_.push(buff) : this.buffs_.push(buff) } const consumable = new Consumable(item.obj.item_id, item.obj.image, item.obj.name) .setEnergy(energy) .setMotivation(motivation) .setHealth(health) .setCount(item.count) this.consumables.push(consumable) } hasEnoughPlugsAndDecorations() { const bottlePlugs = this.consumables.find(c => c.id === 52871000) const decorations = this.consumables.find(c => c.id === 53339000) if ( !bottlePlugs || !decorations ) { new UserMessage("No plugs or decorations were found, defaulting back to selected consumables", UserMessage.TYPE_HINT).show() this.isOptimized_ = false return false } return true } getSelectedConsumables() { if ( this.isOptimized_ ) { const bottlePlugs = this.consumables.find(c => c.id === 52871000) const decorations = this.consumables.find(c => c.id === 53339000) return [bottlePlugs, decorations] } else { return this.consumables.filter(c => c.isSelected === true) } } addSelectedConsumable(val) { if ( typeof val === 'number' ) this.consumables.at(val).isSelected = true if ( val instanceof Consumable ) this.consumables.find(c => c.id === val.id).isSelected = true } get schedule() { return this.schedule_ } set schedule(val) { this.schedule_ = val } get consumables() { return this.consumables_ } set consumables(val) { this.consumables_ = val } get isOptimized() { return this.isOptimized_ } set isOptimized(valOrUpdater) { if ( typeof valOrUpdater === 'function' ) { this.isOptimized_ = valOrUpdater(this.isOptimized_) } else { this.isOptimized_ = valOrUpdater } } get jobsLeftInRound() { return this.jobsLeftInRound_ } set jobsLeftInRound(valOrUpdater) { if ( typeof valOrUpdater === 'function' ) { this.jobsLeftInRound_ = valOrUpdater(this.jobsLeftInRound_) } else { this.jobsLeftInRound_ = valOrUpdater } } get refillsLeft() { return this.schedule.length } get buffs() { return this.buffs_ } get selectedBuffs() { const res = [] for ( const type in this.selectedBuffs_ ) { this.selectedBuffs_[type] && res.push(this.selectedBuffs_[type]) } return res } set selectedBuffs(buff) { const type = buff.getBuffType() if ( this.selectedBuffs_[type]?.id === buff.id ) { this.selectedBuffs_[type] = null return } this.selectedBuffs_[type] = buff new UserMessage(`New ${type} buff selected`, UserMessage.TYPE_SUCCESS).show() } } class ActivityObserver { constructor() { const fiveMinutes = 5 * 60 * 1000 this.activityCheckTimeout_ = null this.refreshCount = 0 this._isEnabled = false this.timeOut_ = fiveMinutes this.selectedConsumables = null } saveTempCookies() { const selectedConsumables = Manager.consumables.reduce((acc, c) => { c.isSelected && acc.push(c.id) return acc }, []) const timeOut = this.timeOut_ const buffs = { travel: Manager.selectedBuffs_.travel?.id || null, character: Manager.selectedBuffs_.character?.id || null } const settings = { statistics: Vajda.statistics, selectedConsumables, buffs, timeOut, direction: Vajda.direction, currentJob: Vajda.currentJob, refreshCount: this.refreshCount + 1, schedule: Manager.schedule, jobsLeftInRound: Manager.jobsLeftInRound, travelSet: Vajda.travelSet, jobSet: Vajda.jobSet, healthSet: Vajda.healthSet } const expiracyDateTemporary = new Date() const hour = expiracyDateTemporary.getHours() expiracyDateTemporary.setHours(2,0,0) if ( hour > 2 ) expiracyDateTemporary.setDate(expiracyDateTemporary.getDate() + 1) document.cookie = `vajdasession=${JSON.stringify(settings)};expires=${expiracyDateTemporary.toGMTString()};` } getTempCookies() { const cookies = document.cookie.split("=") for ( let i = 0; i < cookies.length; i++ ) { if ( cookies[i].includes("vajdasession") ) { const { timeOut, schedule, selectedConsumables, buffs, jobsLeftInRound, refreshCount, ...vajdaSettings } = JSON.parse(cookies[i+1].split(";")[0]) this.selectedConsumables = selectedConsumables this.buffs = buffs this.refreshCount = refreshCount this.timeOut_ = timeOut this.isEnabled = true Manager.jobsLeftInRound = jobsLeftInRound Manager.schedule = schedule Vajda = {...Vajda, ...vajdaSettings, isRunning: true} document.cookie = 'vajdasession=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;' return true } } return false } restartSession() { if ( Vajda.currentState === 2 ) { this.start() return } this.saveTempCookies() location.reload() } resumeSession() { Vajda.findAllConsumables() if ( !this.getTempCookies() ) return Vajda.createWindow(false) Vajda.run() } start(forceStart = false) { if ( !this.isEnabled && !forceStart ) return clearTimeout(this.activityCheckTimeout_) this.activityCheckTimeout_ = setTimeout(this.restartSession.bind(this), this.timeOut_) } stop() { this.isEnabled = false if ( this.activityCheckTimeout_ !== null ) clearTimeout(this.activityCheckTimeout_) this.activityCheckTimeout_ = null } getTimeOut(convertToMinutes) { if ( convertToMinutes ) { return this.timeOut_ / 1000 / 60 } return this.timeOut_ } get isEnabled() { return this._isEnabled } set isEnabled(valOrUpdater) { if ( typeof valOrUpdater === 'function' ) { this._isEnabled = valOrUpdater(this._isEnabled) } else { this._isEnabled = valOrUpdater } } set timeOut(val) { this.timeOut_ = Math.max(Number(val) * 60 * 1000, 2.5 * 60 * 1000) } } window.Manager = new ConsumablesManager() window.Observer = new ActivityObserver() Vajda = { addedJobs: [], allJobs: [], addedJobTablePosition: { content: "0px", scrollbar:"0px" }, consumableSelection: { energy: false, motivation: false, health: false, hideBuffs: false }, consumableTablePosition: { content: '0px', scrollbar: '0px' }, consumableUsed: [], currentJob: { job: 0, direction: true }, currentState: 0, healthSet: -1, language: "", isLoaded: false, isRunning: false, jobFilter: { filterOnlySilver: false, filterNoSilver: false, filterCenterJobs: false, filterJob: "" }, jobSet: -1, jobsLoaded: false, jobTablePosition: { content: "0px", scrollbar: "0px" }, lastJobStartTime: undefined, states: [ 'Idle', 'Running', 'Waiting for a consumable cooldown', 'Calculating optimal route' ], statistics: { sessionJobsCount: 0, sessionXpCount: 0, sessionMoneyCount: 0, totalJobsCount: 0, totalXpCount: 0, totalMoneyCount: 0 }, selectedSet: 0, sets: null, settings: { addEnergy: false, addMotivation: false, addHealth: false, healthStop: 10, setWearDelay: 5, jobDelayMin: 0, jobDelayMax: 0, addDeposit: { isEnabled: false, limit: NaN } }, sortJobTableXp:0, sortJobTableDistance:0, travelSet: -1, regenerationSet: -1, window: null, searchKeys: { "en_DK":{ energy:"Energy", energyText:"Energy increase:", motivation:"Work motivation", motivationText:"Work motivation increase:", health: "Health point bonus", healthText:"Health point bonus:" }, "sk_SK": { energy:"Energie", energyText:"Zvýšenie energie:", motivation:"Pracovnej motivácie", motivationText:"Zvýšenie pracovnej motivácie:", health: "Bonus bodov zdravia", healthText:"Bonus bodov zdravia:", speedText: "Rýchlosť", experience: "skúseností", experienceText: "Skúseností z práce, duelov a boja o pevnosť", money: "peňazí", moneyText: "Peňazí z práce a duelov", luck: "šťastie", luckText: "Vylepšené šťastie", drop: "produktu", dropText: "Vylepšenie šance na získanie produktu" }, "cs_CZ":{ energy:"Energie", energyText:"Zvýšení energie:", motivation:"Pracovní motivace", motivationText:"Zvýšení pracovní motivace:", health: "Bonus zdraví", healthText:"Bonus zdraví:", speedText: "Rychlost", experience: "zkušeností", experienceText: "zkušeností z prací, duelů a bitev o pevnost", money: "prací", moneyText: "$ z prací a duelů", luck: "štěstí", luckText: "Vyšší šance na štěstí", drop: "produktu", dropText: "Vylepšené šance nálezu produktu" }, "hu_HU":{ energy:"Energia növekedése:", energyText:"Energia növekedése:", motivation:"Munka motiváció növelése:", motivationText:"Munka motiváció növelése:", health: "Életerő bónusz", healthText:"Életerő bónusz:" }, "pl_PL":{ energy:"Wzrost energii:", energyText:"Wzrost energii:", motivation:"Zwiększenie motywacji do pracy:", motivationText:"Zwiększenie motywacji do pracy:", health: "Bonus Punktów życia:", healthText:"Bonus Punktów życia:" }, "ro_RO":{ energy:"Energie mărită:", energyText:"Energie mărită:", motivation:"Creştere a motivaţiei de muncă:", motivationText:"Creştere a motivaţiei de muncă:", health: "Puncte de viaţă:", healthText:"Puncte de viaţă:" } } } Vajda.isNumber = function(potentialNumber) { return Number.isInteger(parseInt(potentialNumber)) } Vajda.RNG = function(min, max) { let minN = Math.min(min, max) let maxN = Math.max(min, max) let number = Math.floor((minN + Math.random() * (maxN - minN + 1))) return number } Vajda.isAllowedToDepositMoney = function() { const hasMoreMoneyThanLimit = Vajda.settings.addDeposit.limit <= Character.money const hasHomeTown = Character.homeTown.town_id !== 0 if ( hasHomeTown == false ) { new UserMessage("You don't have a home town", UserMessage.TYPE_HINT).show() return false } return Vajda.settings.addDeposit.isEnabled && hasMoreMoneyThanLimit } Vajda.getCookies = function() { const cookies = document.cookie.split("=") for ( let i = 0; i < cookies.length; i++ ) { if ( cookies[i].includes("vajdatemporary") ) { const obj = cookies[i+1].split(";") const tempObject = JSON.parse(obj[0]) const tmpAddedJobs = tempObject.addedJobs for ( const job of tmpAddedJobs ) { const newJob = new Job(job.x, job.y, job.id, job.groupId) newJob .setIsSilver(job.isSilver) .setExperience(job.experience) .setMoney(job.money) .setMotivation(job.motivation) .setStopMotivation(job.stopMotivation) .setSet(job.set) Vajda.addedJobs.push(newJob) } Vajda.travelSet = tempObject.travelSet Vajda.jobSet = tempObject.jobSet Vajda.healthSet = tempObject.healthSet Vajda.currentJob = tempObject.currentJob Vajda.setSetForAllJobs() } if ( cookies[i].includes("vajdapermanent") ) { const obj = cookies[i+1].split(";") const permanentObject = JSON.parse(obj[0]) const { isOptimized, isEnabled, timeOut, ...settings } = permanentObject.settings Manager.isOptimized = !!isOptimized Observer.isEnabled = !!isEnabled Observer.timeOut = timeOut || 5 Vajda.settings = settings if ( !settings.addDeposit ) { Vajda.settings.addDeposit = { isEnabled: false, limit: NaN } } Vajda.statistics.totalJobsCount = Math.floor(permanentObject.totalJobs) Vajda.statistics.totalXpCount = permanentObject.totalXp Vajda.statistics.totalMoneyCount = permanentObject.totalMoney } } } Vajda.setCookies = function() { let expiracyDateTemporary = new Date() let hour = expiracyDateTemporary.getHours() expiracyDateTemporary.setHours(2,0,0) if ( hour > 2 ) expiracyDateTemporary.setDate(expiracyDateTemporary.getDate() + 1) const addedJobs = Vajda.addedJobs.map(j => ({ x: j.x, y: j.y, id: j.id, groupId: j.groupId, isSilver: j.isSilver, experience: j.experience, money: j.money, motivation: j.motivation, stopMotivation: j.stopMotivation, set: j.set })) let temporaryObject = { addedJobs, travelSet: Vajda.travelSet, jobSet: Vajda.jobSet, healthSet: Vajda.healthSet, currentJob: Vajda.currentJob } let expiracyDatePernament = new Date() expiracyDatePernament.setDate(expiracyDatePernament.getDate() + 360000) let pernamentObject = { settings: {...Vajda.settings, isOptimized: Manager.isOptimized, isEnabled: Observer.isEnabled, timeOut: Observer.getTimeOut(true)}, totalJobs: Vajda.statistics.totalJobsCount, totalXp: Vajda.statistics.totalXpCount, totalMoney: Vajda.statistics.totalMoneyCount } const jsonTemporary = JSON.stringify(temporaryObject) const jsonPernament = JSON.stringify(pernamentObject) document.cookie = `vajdatemporary=${jsonTemporary};expires=${expiracyDateTemporary.toGMTString()};` document.cookie = `vajdapermanent=${jsonPernament};expires=${expiracyDatePernament.toGMTString()};` } Vajda.shouldEquipHealthSet = function(consumable) { if ( !consumable.hasCooldown() ) { return false } if ( Manager.isOptimized ) { return false } return consumable.health > 0 && Vajda.healthSet > -1 } Vajda.parseConsumableBonuses = function(bonuses) { let getBonus = (text,type) => { switch(type) { case 0: text = text.replace(Vajda.searchKeys[Vajda.language].energyText,"") break case 1: text = text.replace(Vajda.searchKeys[Vajda.language].motivationText,"") break case 2: text = text.replace(Vajda.searchKeys[Vajda.language].healthText,"") break } text = text.slice(1) text = text.replace("%","") return parseInt(text) } let result = Array(3).fill(0) for ( let i = 0 ; i < bonuses.length; i++ ) { let type = -1 if ( bonuses[i].includes(Vajda.searchKeys[Vajda.language].energyText) ) { type = 0 }else if ( bonuses[i].includes(Vajda.searchKeys[Vajda.language].motivationText) ) { type = 1 }else if ( bonuses[i].includes(Vajda.searchKeys[Vajda.language].healthText) ) { type = 2 } if ( type !=-1 ) result[type] = getBonus(bonuses[i],type) } return result } Vajda.findAllConsumables = function() { if ( Vajda.searchKeys[Vajda.language] === undefined ) return const energyConsumables = Bag.search(Vajda.searchKeys[Vajda.language].energy) for ( const consumable of energyConsumables ) { Manager.addNewConsumable(consumable) } const motivationConsumables = Bag.search(Vajda.searchKeys[Vajda.language].motivation) for ( const consumable of motivationConsumables ) { Manager.addNewConsumable(consumable) } const healthConsumables = Bag.search(Vajda.searchKeys[Vajda.language].health) for ( const consumable of healthConsumables ) { Manager.addNewConsumable(consumable) } const speedConsumables = Bag.search(Vajda.searchKeys[Vajda.language].speedText) for ( const consumable of speedConsumables ) { if ( consumable.obj.usetype !== 'none' ) { Manager.addNewConsumable(consumable) } } const luckConsumables = Bag.search(Vajda.searchKeys[Vajda.language].luck) for ( const consumable of luckConsumables ) { if ( consumable.obj.usetype !== 'none' ) { Manager.addNewConsumable(consumable) } } const experienceConsumables = Bag.search(Vajda.searchKeys[Vajda.language].experience) for ( const consumable of experienceConsumables ) { if ( consumable.obj.usetype !== 'none' ) { Manager.addNewConsumable(consumable) } } const moneyConsumables = Bag.search(Vajda.searchKeys[Vajda.language].money) for ( const consumable of moneyConsumables ) { if ( consumable.obj.usetype !== 'none' ) { Manager.addNewConsumable(consumable) } } const dropConsumables = Bag.search(Vajda.searchKeys[Vajda.language].drop) for ( const consumable of dropConsumables ) { if ( consumable.obj.usetype !== 'none' ) { Manager.addNewConsumable(consumable) } } Observer.selectedConsumables?.forEach(id => { Manager.consumables.find(c => c.id === id).setIsSelected(true) }) for ( const type in Observer.buffs ) { Manager.selectedBuffs_[type] = Manager.buffs_.find(b => b.id === Observer.buffs[type]) || null } Observer.selectedConsumables = null Observer.buffs = null } Vajda.filterConsumables = function(energy, motivation, health, hideBuffs) { const result = hideBuffs ? [] : [...Manager.buffs] for ( const consumable of Manager.consumables ) { if ( energy && consumable.energy === 0 ) { continue } if ( motivation && consumable.motivation === 0 ) { continue } if ( health && consumable.health === 0 ) { continue } result.push(consumable) } return result } Vajda.changeConsumableSelection = function(id, isSelected) { Manager.consumables.find(c => c.id === id)?.setIsSelected(isSelected) } Vajda.changeSelectionAllConsumables = function(selected) { for ( const consumable of Manager.consumables ) { consumable.setIsSelected(selected) } } Vajda.loadJobs = function() { if ( !Vajda.jobsLoaded ) { new UserMessage("Loading...", UserMessage.TYPE_HINT).show() let index = 0 let currentLength = 0 let maxLength = 299 Ajax.get('map', 'get_minimap', {}, function(r) { var tiles = [] var jobs = [] for ( let townNumber in r.towns ) { if ( r.towns[townNumber].town_id === Character.homeTown.town_id ) { Vajda.homeTown = r.towns[townNumber] break } } for ( let jobGroup in r.job_groups ) { const groupId = parseInt(jobGroup) let group = r.job_groups[jobGroup] let jobsGroup = JobList.getJobsByGroupId(groupId) for ( let tilecoord = 0; tilecoord < group.length; tilecoord++ ) { let xCoord = Math.floor(group[tilecoord][0]/GameMap.tileSize) let yCoord = Math.floor(group[tilecoord][1]/GameMap.tileSize) if ( currentLength == 0 ) { tiles[index] = [] } tiles[index].push([xCoord,yCoord]) currentLength++ if ( currentLength === maxLength ) { currentLength = 0 index++ } for ( let i = 0; i < jobsGroup.length; i++ ) { jobs.push(new Job(group[tilecoord][0],group[tilecoord][1],jobsGroup[i].id, groupId)) } } } let toLoad = tiles.length let loaded = 0 for ( let blocks = 0; blocks < tiles.length; blocks++ ) { GameMap.Data.Loader.load(tiles[blocks], function() { loaded++ if ( loaded === toLoad ) { Vajda.jobsLoaded = true Vajda.allJobs = jobs Vajda.findAllConsumables() Vajda.createWindow() } }) } }) } else { Vajda.findAllConsumables() Vajda.createWindow() } } Vajda.loadJobData = function(callback) { Ajax.get('work','index', {}, function(r) { if( r.error ) { console.log(r.error) return } JobsModel.initJobs(r.jobs) callback() }) } Vajda.getJobSet = function(x, y, id) { const job = Vajda.findAddedJob(x, y, id) if ( job !== null ) return job.set } Vajda.setJobSet = function(x,y,id,set) { const job = Vajda.findAddedJob(x, y, id) if ( job !== null) return job.setSet(set) } Vajda.findAddedJob = function(x, y, id) { for ( const job of Vajda.addedJobs ) { if ( job.x === x && job.y === y && job.id === id ) { return job } } return null } Vajda.loadSets = async function(callback) { Ajax.remoteCallMode('inventory', 'show_equip', {}, function(r) { Vajda.sets = r.data if ( callback !== undefined ) callback() }) } Vajda.loadJobMotivation = function(index, callback) { const job = Vajda.addedJobs.at(index) Ajax.get('job', 'job', {jobId: job.id, x: job.x, y: job.y}, function(r) { if ( callback !== undefined ) callback(r.motivation*100) }) } Vajda.loadLanguage = function() { Ajax.remoteCall("settings", "settings", {}, function(resp) { Vajda.language = resp.lang.account.key }) } Vajda.getConsumableIcon = function(src) { return `<div><img src='${src}'></div>` } Vajda.getItemImage = function(id) { return ItemManager.get(id).wear_image } Vajda.createComboxJobSets = function(x, y, id) { let combobox = new west.gui.Combobox() Vajda.addComboboxItems(combobox) combobox = combobox.select(Vajda.getJobSet(x, y, id)) combobox.setWidth(60) combobox.addListener(function(value) { Vajda.setJobSet(x, y, id, value) Vajda.selectTab("chosenJobs") }); return combobox.getMainDiv() } Vajda.addComboboxItems = function(combobox) { combobox.addItem(-1, "None") for ( let i = 0 ; i < Vajda.sets.length; i++) { combobox.addItem(i.toString(), Vajda.sets[i].name) } } Vajda.updateJobDistances = function() { for ( let i = 0; i < Vajda.allJobs.length; i++ ) { Vajda.allJobs[i].calculateDistance() } } Vajda.findJobData = function(job) { for ( let i = 0 ;i < JobsModel.Jobs.length; i++ ) { if ( JobsModel.Jobs[i].id === job.id ) { return JobsModel.Jobs[i] } } } Vajda.findJob = function(x, y, id) { for ( let i = 0; i < Vajda.allJobs.length; i++ ) { if ( Vajda.allJobs[i].id === id && Vajda.allJobs[i].x === x && Vajda.allJobs[i].y === y ) { return Vajda.allJobs[i] } } } Vajda.addJob = function(x, y, id) { if ( !Vajda.checkIfJobAdded(id) ) { Vajda.addedJobs.push(Vajda.findJob(x, y, id)) } } Vajda.removeJob = function(x, y, id) { for ( let i = 0; i < Vajda.addedJobs.length; i++ ) { if ( Vajda.addedJobs[i].id === id && Vajda.addedJobs[i].x === x && Vajda.addedJobs[i].y === y) { Vajda.addedJobs.splice(i,1) Vajda.consolidePosition(i) break } } } Vajda.parseJobData = function(jobs) { for ( let job = 0; job < jobs.length; job++ ) { let currentJob = jobs[job] let data = Vajda.findJobData(currentJob) let xp = data.basis.short.experience let money = data.basis.short.money currentJob.setMotivation(data.jobmotivation*100) if ( currentJob.isSilver ) { xp = Math.ceil(1.5*xp) money = Math.ceil(1.5*money) } currentJob.setExperience(xp) currentJob.setMoney(money) } } Vajda.getJobName = function(id) { return JobList.getJobById(id).name } Vajda.getJobIcon = function(isSilver, id, x, y) { return ` <div class="job" style="left: 0; top: 0; position: relative;"> <div onclick="JobWindow.open(${id}, ${x}, ${y})" class="featured ${isSilver && 'silver'}"></div> <div class='centermap' onclick='GameMap.center(${x}, ${y})' style="position: absolute; background-image: url('../images/map/icons/instantwork.png'); width: 20px; height: 20px; top: 0; right: 3px; cursor: pointer"></div> <img src="../images/jobs/${JobList.getJobById(id).shortname}.png" class="job_icon" alt='job_image'> </div> ` } Vajda.checkIfJobAdded = function(id) { for ( const job of Vajda.addedJobs ) { if ( job.id === id ) { return true } } return false } Vajda.isJobSilver = function(x, y, id) { const key = `${x}-${y}` const jobData = GameMap.JobHandler.Featured[key] if ( jobData === undefined || jobData[id] === undefined ) { return false } else { return jobData[id].silver } } Vajda.compareUniqueJobs = function(job, jobs){ for ( let i = 0 ; i < jobs.length; i++ ) { if ( jobs[i].id === job.id ) { if ( job.isSilver && !jobs[i].isSilver || (job.isSilver === jobs[i].isSilver && job.distance < jobs[i].distance) ) { const j = jobs.at(i) if ( !Vajda.isJobSilver(j.x, j.y, j.id) ) jobs.splice(i,1) jobs.push(job) } return } } jobs.push(job) } Vajda.getAllUniqueJobs = function() { Vajda.updateJobDistances() let jobs = [] for ( let i = 0 ; i < Vajda.allJobs.length; i++ ) { const currentJob = Vajda.allJobs.at(i) if ( Vajda.jobFilter.filterJob !== "" ) { if ( !Vajda.getJobName(currentJob.id).toLowerCase().includes(Vajda.jobFilter.filterJob)) { continue } } if ( Vajda.checkIfJobAdded(currentJob.id) ) { continue } let isSilver = Vajda.isJobSilver(currentJob.x, currentJob.y, currentJob.id) currentJob.isSilver = isSilver currentJob.calculateDistance() if ( isSilver && Vajda.jobFilter.filterNoSilver ) { continue } if ( !isSilver && Vajda.jobFilter.filterOnlySilver ) { continue } if ( Vajda.jobFilter.filterCenterJobs && currentJob.id < 131 ) { continue } Vajda.compareUniqueJobs(currentJob, jobs) } Vajda.parseJobData(jobs) let experienceSort = function(a, b) { if ( a === null && b === null ) { return 0 } if ( a === null && b !== null ) { return 1 } if ( a !== null && b === null ) { return -1 } let a1 = a.experience let b1 = b.experience return (a1 > b1) ? -1 :(a1 < b1) ? 1 :0 } let reverseExperienceSort = function(a, b) { if ( a === null && b === null ) { return 0 } if ( a === null && b !== null ) { return -1 } if ( a !== null && b === null ) { return 1 } let a1 = a.experience let b1 = b.experience return (a1 > b1) ? 1 :(a1 < b1) ? -1 :0 } let distanceSort = function(a, b) { if ( a === null && b === null ) { return 0 } if ( a === null && b !== null ) { return 1 } if ( a !== null && b === null ) { return -1 } let a1 = a.distance let b1 = b.distance return (a1 > b1) ? -1 :(a1 < b1) ? 1 :0 } let reverseDistanceSort = function(a, b) { if ( a === null && b === null ) { return 0 } if ( a === null && b !== null ) { return -1 } if(a !== null && b === null ) { return 1 } let a1 = a.distance let b1 = b.distance return (a1 > b1) ? 1 :(a1 < b1) ? -1 :0 } if ( Vajda.sortJobTableXp === 1 ) { jobs.sort(experienceSort) } if ( Vajda.sortJobTableXp === -1 ) { jobs.sort(reverseExperienceSort) } if ( Vajda.sortJobTableDistance === 1 ) { jobs.sort(distanceSort) } if ( Vajda.sortJobTableDistance === -1 ) { jobs.sort(reverseDistanceSort) } return jobs } Vajda.parseStopMotivation = function() { for ( let i = 0; i < Vajda.addedJobs.length; i++ ) { let stopMotivation = $(".vajda-window #x-" + Vajda.addedJobs[i].x + "y-" + Vajda.addedJobs[i].y + "id-" + Vajda.addedJobs[i].id).prop("value") if ( Vajda.isNumber(stopMotivation) ) { Vajda.addedJobs[i].setStopMotivation(parseInt(stopMotivation)) } else { return false } } return true } Vajda.setSetForAllJobs = function() { for ( let i = 0; i < Vajda.addedJobs.length; i++ ) { Vajda.addedJobs[i].setSet(Vajda.jobSet) } } Vajda.consolidePosition = function(removeIndex) { if ( removeIndex <= Vajda.currentJob.job && Vajda.currentJob.job > 0 ) { Vajda.currentJob.job-- } if ( Vajda.addedJobs.length === 1 ) { Vajda.currentJob.direction = true } } Vajda.createDistanceMatrix = function() { const matrix = new Array(Vajda.addedJobs.length) for ( let i = 0; i < matrix.length; i++ ) { matrix[i] = Vajda.addedJobs[i].calculateJobDistances() } return matrix } Vajda.countSetBits = function(n) { let count = 0 while (n) { n &= n - 1 count++ } return count } Vajda.heldKarpSymmetric = function(distances, startJob) { const n = distances.length const memo = Array(1 << n).fill().map(() => Array(n).fill({ cost: Infinity, path: [] })) memo[1 << startJob][startJob] = { cost: 0, path: [startJob] } for ( let subsetSize = 2; subsetSize <= n; subsetSize++ ) { for ( let subset = 0; subset < (1 << n); subset++ ) { if ( Vajda.countSetBits(subset) === subsetSize && (subset & (1 << startJob)) ) { for ( let end = 0; end < n; end++ ) { if ( (subset & (1 << end)) !== 0 ) { for ( let prevEnd = 0; prevEnd < n; prevEnd++ ) { if ( prevEnd !== end && (subset & (1 << prevEnd)) !== 0 ) { const newCost = memo[subset ^ (1 << end)][prevEnd].cost + distances[prevEnd][end] if (newCost < memo[subset][end].cost) { memo[subset][end] = { cost: newCost, path: memo[subset ^ (1 << end)][prevEnd].path.concat([end]) } } } } } } } } } let minCost = Infinity let minPath = [] for ( let end = 0; end < n; end++ ) { if ( end !== startJob && memo[(1 << n) - 1][end].cost < minCost ) { minCost = memo[(1 << n) - 1][end].cost minPath = memo[(1 << n) - 1][end].path } } return { cost: minCost, path: minPath } } Vajda.setEntryPoint = function(route) { const firstJob = route.at(0) const lastJob = route.at(-1) if ( firstJob.calculateDistance() > lastJob.calculateDistance() ) route.reverse() //i could in theory make vajda start with the job nearest to current character position but cba } Vajda.getOptimalRoute = function(distanceMatrix) { const jobsCount = distanceMatrix.length if ( jobsCount === 1 ) return { cost: 0, path: [0] } const routes = [] for ( let startJob = 0; startJob < jobsCount; startJob++ ) { const { cost, path } = Vajda.heldKarpSymmetric(distanceMatrix, startJob) routes.push({ cost, path }) } return routes.reduce(function(prev, curr) { return prev.cost < curr.cost ? prev : curr }) } Vajda.createRoute = function() { Vajda.currentJob = { job: 0, direction: true } const distanceMatrix = Vajda.createDistanceMatrix() const optimalRoute = Vajda.getOptimalRoute(distanceMatrix) const addedJobsOrder = [] for ( const index of optimalRoute.path ) { addedJobsOrder.push(Vajda.addedJobs.at(index)) } Vajda.setEntryPoint(addedJobsOrder) Vajda.addedJobs = addedJobsOrder Vajda.selectTab("chosenJobs") } Vajda.createSetGui = function() { if ( Vajda.sets.length === 0 ) { return $(`<span style='font-size: 20px'>No sets available</span>`) } let htmlSkel = $(` <div id='vajda_sets_window' style='display: block; position: relative; width: 650px; height:430px'> <div id='vajda_sets_left' style='display: block; position: absolute; width: 250px; height: 430px; top:0px; left:0px'></div> <div id='vajda_sets_right' style='display: block; position: absolute; width:300px; height: 410px; top: 0px; left: 325px'></div> </div>`) let combobox = new west.gui.Combobox("combobox_sets") Vajda.addComboboxItems(combobox) combobox = combobox.select(Vajda.selectedSet) combobox.addListener(function(value) { Vajda.selectedSet = value Vajda.selectTab("sets") }) let buttonSelectTravelSet = new west.gui.Button("Select travel set", function() { Vajda.travelSet = Vajda.selectedSet Vajda.selectTab("sets") }) let buttonSelectJobSet = new west.gui.Button("Select job set", function() { Vajda.jobSet = Vajda.selectedSet Vajda.setSetForAllJobs() Vajda.selectTab("sets") }) let buttonSelectHealthSet = new west.gui.Button("Select health set", function() { Vajda.healthSet = Vajda.selectedSet Vajda.selectTab("sets") }) let buttonSelectRegenSet = new west.gui.Button("Select regeneration set", function() { Vajda.regenerationSet = Vajda.selectedSet Vajda.selectTab("sets") }) let travelSetText = "None" if ( Vajda.travelSet != -1 ) { travelSetText = Vajda.sets[Vajda.travelSet].name } let jobSetText = "None" if ( Vajda.jobSet != -1 ) { jobSetText = Vajda.sets[Vajda.jobSet].name } let healthSetText = "None" if ( Vajda.healthSet != -1 ) { healthSetText = Vajda.sets[Vajda.healthSet].name } let regenSetText = "None" if ( Vajda.regenerationSet != -1 ) { regenSetText = Vajda.sets[Vajda.regenerationSet].name } const left = $("<div></div>") .append( new west.gui.Groupframe() .appendToContentPane($("<span>Sets</span><br><br>")) .appendToContentPane(combobox.getMainDiv()) .appendToContentPane($("<br><br><span>Travel set:"+ travelSetText +"</span><br><br>")) .appendToContentPane(buttonSelectTravelSet.getMainDiv()) .appendToContentPane($("<br><br><span>Job set:"+ jobSetText +"</span><br><br>")) .appendToContentPane(buttonSelectJobSet.getMainDiv()) .appendToContentPane($("<br><br><span>Health set:"+ healthSetText +"</span><br><br>")) .appendToContentPane(buttonSelectHealthSet.getMainDiv()) .getMainDiv() ) const right = $("<div style=\'display:block; position:relative; width:300px; height:410px'\></div>") //head div right.append("<div class=\'wear_head wear_slot'\ style=\'display:block; position:absolute; left:30px; top:1px; width:93px; height:94px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position: -95px 0'\></div>") //chest div right.append("<div class=\'wear_body wear_slot'\ style=\'display:block; position:absolute; left:30px; top:106px; width:95px; height:138px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:0 0'\></div>") //pants div right.append("<div class=\'wear_pants wear_slot'\ style=\'display:block; position:absolute; left:30px; top:258px; width:93px; height:138px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:0 0'\></div>") //neck div right.append("<div class=\'wear_neck wear_slot'\ style=\'display:block; position:absolute; left:-47px; top:1px; width:74px; height:74px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:-189px 0'\></div>") //right arm div right.append("<div class=\'wear_right_arm wear_slot'\ style=\'display:block; position:absolute; left:-64px; top:79px; width:95px; height:138px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:0 0'\></div>") //animal div right.append("<div class=\'wear_animal wear_slot'\ style=\'display:block; position:absolute; left:-64px; top:223px; width:93px; height:94px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:-95px 0'\></div>") //yield div right.append("<div class=\'wear_yield wear_slot'\ style=\'display:block; position:absolute; left:-47px; top:321px; width:74px; height:74px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:-189px 0'\></div>") //left arm div right.append("<div class=\'wear_left_arm wear_slot'\ style=\'display:block; position:absolute; left:127px; top:52px; width:95px; height:138px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:0 0'\></div>") //belt div right.append("<div class=\'wear_belt wear_slot'\ style=\'display:block; position:absolute; left:127px; top:200px; width:93px; height:94px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:-95px 0'\></div>") //boots div right.append("<div class=\'wear_foot wear_slot'\ style=\'display:block; position:absolute; left:127px; top:302px; width:93px; height:94px; background:url(https://westzz.innogamescdn.com/images/window/wear/bg_sprite.png) 0 0 no-repeat; background-position:-95px 0'\></div>") let keys = ["head","body","pants","neck","right_arm","animal","yield","left_arm","belt","foot"] if ( Vajda.selectedSet !== -1 ) Vajda.insertSetImages(right,keys) $("#vajda_sets_left",htmlSkel).append(left) $("#vajda_sets_right",htmlSkel).append(right) return htmlSkel } Vajda.getImageSkel = function() { return $("<img src=\''\>") } Vajda.insertSetImages = function(html,keys) { for ( let i = 0; i < keys.length; i++ ) { if ( Vajda.sets[Vajda.selectedSet][keys[i]] !== null ) { $(".wear_"+keys[i], html).append(Vajda.getImageSkel().attr("src", Vajda.getItemImage(Vajda.sets[Vajda.selectedSet][keys[i]]))) } } return html } Vajda.addEventsHeader = function() { $(".vajda-window .jobXp").click(function() { if ( Vajda.sortJobTableXp === 0 ) { Vajda.sortJobTableXp = 1 } else { ( Vajda.sortJobTableXp === 1 ) ? Vajda.sortJobTableXp = -1 : Vajda.sortJobTableXp = 1 } Vajda.sortJobTableDistance = 0 Vajda.selectTab("jobs") }) $(".vajda-window .jobDistance").click(function() { if ( Vajda.sortJobTableDistance === 0 ) { Vajda.sortJobTableDistance = 1 } else { ( Vajda.sortJobTableDistance === 1 ) ? Vajda.sortJobTableDistance = -1 : Vajda.sortJobTableDistance = 1 } Vajda.sortJobTableXp = 0 Vajda.selectTab("jobs") }) } Vajda.isInHomeTown = function() { const homeTown = Character.homeTown return GameMap.calcWayTime(Character.position,{x: homeTown.x, y: homeTown.y}) == 0 } Vajda.addDeposit = async function(townId) { const amount = Character.money await Ajax.remoteCall("building_bank", "deposit", { town_id: townId, amount: amount }, function(data) { if (data.error == false) { BankWindow.Balance.Mupdate(data) Character.setDeposit(data.deposit) Character.setMoney(data.own_money) //new UserMessage(s(sextext('Vložil si $ %1 a zaplatil si poplatok $ %2', 'Vložila si $ %1 a zaplatila si poplatok $ %2', Character.charSex), amount, data.fee),UserMessage.TYPE_SUCCESS).show() } else new UserMessage(data.msg,UserMessage.TYPE_ERROR).show() }, BankWindow) } Vajda.goDepositMoney = async function() { const townId = Character.homeTown.town_id if ( !townId ) return await Vajda.equipSet(Vajda.travelSet) TaskQueue.add(new TaskWalk(townId, 'town')) while(true) { if ( Vajda.isInHomeTown() ) { break } if( !Vajda.isRunning ) { break } await sleep(1000) } await Vajda.addDeposit(townId) $('.tw2gui_dialog_framefix').remove() } Vajda.updateJobsMotivationOnRefill = function(val) { return Vajda.addedJobs.map(job => job.setMotivation(p => p + val).motivation - job.stopMotivation) } Vajda.checkMotivation = async function(index, result, callback) { let check = function(index, result) { Vajda.loadJobMotivation(index, function(motivation) { Vajda.addedJobs.at(index).setMotivation(motivation) result.push(motivation) if ( index + 1 < Vajda.addedJobs.length ) { check(++index, result) } else if( index + 1 === Vajda.addedJobs.length ) { callback(result) return } }) } check(index, result) } Vajda.isMotivationAbove = function(result) { for ( let i = 0; i < result.length; i++ ) { if ( result.at(i) > Vajda.addedJobs.at(i).stopMotivation ) { return true } } return false } Vajda.isStopMotivationZero = function() { for ( let i = 0; i < Vajda.addedJobs.length; i++ ) { if( Vajda.addedJobs[i].stopMotivation === 0 ) { return true } } return false } Vajda.isHealthBelowLimit = function() { if ( Vajda.settings.healthStop >= ((Character.health/Character.maxHealth) * 100) ) { return true } return false } Vajda.changeJob = function() { Vajda.currentJob.direction ? Vajda.currentJob.job++ : Vajda.currentJob.job--; if ( Vajda.currentJob.job === Vajda.addedJobs.length ) { Vajda.currentJob.job-- Vajda.currentJob.direction = false } else if ( Vajda.currentJob.job < 0 ) { Vajda.currentJob.job++ Vajda.currentJob.direction = true } Vajda.setCookies() Vajda.run() } Vajda.searchBest = function(skills, jobId, onlyWearable = true) { if (!Bag.loaded) { EventHandler.listen('inventory_loaded', function() { Vajda.searchBest(skills, jobId, onlyWearable) return EventHandler.ONE_TIME_EVENT }) return } let set = west.item.Calculator.getBestSet(skills, jobId), items = set && set.getItems() || [], invItems = Bag.getItemsByItemIds(items), result = [], i, invItem, wearItem for (i = 0; i < invItems.length; i++) { invItem = invItems[i] wearItem = Wear.get(invItem.getType()) if (!wearItem || (wearItem && (wearItem.getItemBaseId() !== invItem.getItemBaseId() || wearItem.getItemLevel() < invItem.getItemLevel()))) { result.push(invItem) } } return result.map(item => item.obj.getId()) } Vajda.getBestGear = function(jobid) { let modelId = function(jobid) { for ( let i = 0; i < JobsModel.Jobs.length; i++ ) { if ( JobsModel.Jobs[i].id === jobid ) return i } return -1 } const result = west.item.Calculator.getBestSet(JobsModel.Jobs[modelId(jobid)].get('skills'), jobid) const bestItems = result && result.getItems() return bestItems } Vajda.isWearing = function(itemId) { if ( Wear.wear[ItemManager.get(itemId).type] === undefined) return false return Wear.wear[ItemManager.get(itemId).type].obj.item_id == itemId } Vajda.isGearEquiped = async function(items) { for ( const itemId of items ) { if ( !Vajda.isWearing(itemId) ) { return false } } return true } Vajda.equipBestGear = async function(job) { const bestGear = job.getBestEquipment() for ( const itemId of bestGear ) { Wear.carry(Bag.getItemByItemId(itemId)) } while (true) { const isFinished = await Vajda.isGearEquiped(bestGear) if ( isFinished ) break await sleep(50) } return Promise.resolve(true) } Vajda.getSetItemArray = function(set) { var items = [] if ( set.head !== null ) items.push(set.head) if ( set.neck !== null ) items.push(set.neck) if ( set.body !== null ) items.push(set.body) if ( set.right_arm !== null ) items.push(set.right_arm) if ( set.left_arm !== null ) items.push(set.left_arm) if ( set.belt !== null ) items.push(set.belt) if ( set.foot !== null ) items.push(set.foot) if ( set.animal !== null ) items.push(set.animal) if ( set.yield !== null ) items.push(set.yield) if ( set.pants !== null ) items.push(set.pants) return items } Vajda.equipSet = async function(set) { if ( set === -1 ) return true EquipManager.switchEquip(Vajda.sets[set].equip_manager_id) while ( true ) { let isFinished = await Vajda.isGearEquiped(Vajda.getSetItemArray(Vajda.sets[set])) if ( isFinished ) break await sleep(50) } return Promise.resolve(true) } Vajda.cancelJobs = function() { if ( TaskQueue.queue.length > 0 ) TaskQueue.cancelAll() } //https://prnt.sc/KAgbLNqB4zK6 Vajda.runJob = async function(jobIndex, jobCount) { Vajda.statistics.sessionJobsCount += jobCount Vajda.statistics.totalJobsCount += jobCount const oldXp = Character.experience const oldMoney = Character.money const job = Vajda.addedJobs.at(jobIndex) await Vajda.equipBestGear(job) for ( let i = 0; i < jobCount; i++ ) { JobWindow.startJob(job.id, job.x, job.y, 15) } Observer.start() await sleep(Vajda.settings.setWearDelay * 1000) Vajda.equipSet(job.set) Manager.useBuff('character') while (true) { if ( TaskQueue.queue.length === 0 ) { Vajda.updateStatistics(oldXp, oldMoney) Vajda.setCookies() Vajda.prepareJobRun(jobIndex) return } if ( !Vajda.isRunning || Vajda.isHealthBelowLimit() ) { break } await sleep(1000) } Vajda.statistics.sessionJobsCount -= TaskQueue.queue.length Vajda.statistics.totalJobsCount -= TaskQueue.queue.length Vajda.updateStatistics(oldXp, oldMoney) Vajda.setCookies() Vajda.cancelJobs() if ( Vajda.isRunning && Vajda.isHealthBelowLimit() ) { await sleep(2000) Vajda.run() } } Vajda.walkToJob = async function(index) { const job = Vajda.addedJobs.at(index) const jobGroup = JobList.getJobsByGroupId(job.groupId) const jobToWalkTo = jobGroup.map(j => j.id ).map( id => { const j = JobsModel.getById(id) return { id: id, workpoints: j.workpoints, //required LP jobpoints: j.jobpoints //character has LP } }).reduce((prev, curr) => { return prev.workpoints < curr.workpoints ? prev : curr }) await Manager.useBuff('travel') if ( Vajda.isAllowedToDepositMoney() ) { Observer.start() await Vajda.goDepositMoney() } JobWindow.startJob(jobToWalkTo.id, job.x, job.y, 15) Observer.start() while (true) { if ( GameMap.calcWayTime(Character.position, {x: job.x, y: job.y} ) === 0 ) { break } if ( !Vajda.isRunning ) { break } await sleep(1000) } Vajda.cancelJobs() if ( Vajda.isRunning ) Vajda.prepareJobRun(index) } Vajda.prepareJobRun = async function(index) { const job = Vajda.addedJobs.at(index) setTimeout(function() { Vajda.loadJobMotivation(index, async function(motivation) { if ( Character.energy === 0 || Vajda.isHealthBelowLimit() ) { Vajda.run() } else if ( motivation <= job.stopMotivation && job.stopMotivation > 0 ) { Vajda.checkMotivation(0, [], function(result) { if ( Vajda.isMotivationAbove(result) ) { Vajda.changeJob() } else { Vajda.run() } }) } else if ( GameMap.calcWayTime(Character.position,{x: job.x, y: job.y}) === 0 ) { let maxJobs = Premium.hasBonus('automation') ? 9 : 4 let numberOfJobs if (job.stopMotivation !== 0 ) { numberOfJobs = Math.min(Math.min(motivation - job.stopMotivation, Character.energy), maxJobs) } else { numberOfJobs = Math.min(Character.energy, maxJobs) } Vajda.runJob(index, Math.floor(numberOfJobs)) if ( Manager.isOptimized ) Manager.checkSchedule(numberOfJobs) } else { await Vajda.equipSet(Vajda.travelSet) Vajda.walkToJob(index) } }) }, Vajda.RNG(Vajda.settings.jobDelayMin, Vajda.settings.jobDelayMax) * 1000) } Vajda.canAddMissing = function(result) { if ( !Vajda.settings.addMotivation && Vajda.jobsBelowMotivation(result) && !Vajda.isStopMotivationZero() ) { alert("Can't continue because of motivation") return false } if ( !Vajda.settings.addEnergy && Character.energy === 0 ) { alert("Can't continue because of energy") return false } if ( !Vajda.settings.addHealth && Vajda.isHealthBelowLimit() ) { alert("Can't continue because of health") return false } return true } Vajda.finishRun = function() { Vajda.currentState = 0 Vajda.isRunning = false Vajda.selectTab("chosenJobs") alert("Finished") } Vajda.jobsBelowMotivation = function(result) { let count = 0 for ( let i = 0; i < result.length; i++ ) { if ( result[i] <= Vajda.addedJobs[i].stopMotivation ) { count++ } } return count } Vajda.averageMissingMotivation = function(result) { let motivation = 0 for ( let i = 0; i < result.length; i++ ) { motivation += 100-result[i] } return motivation/result.length } Vajda.fillUp = async function(result) { const energyMissing = 100 - (Character.energy/Character.maxEnergy) * 100 const motivationMissing = Vajda.jobsBelowMotivation(result) const averageMotivationMissing = Vajda.averageMissingMotivation(result) const consumableToUse = Manager.findProperConsumable(motivationMissing, energyMissing, averageMotivationMissing) if ( consumableToUse === null ) return false await Manager.useConsumableOrWaitForCooldown(consumableToUse, true) if ( Manager.isOptimized && consumableToUse.isCakeDecoration() ) { const updatedMotivation = Vajda.updateJobsMotivationOnRefill(consumableToUse.motivation) await Manager.createSchedule(updatedMotivation) } return true } Vajda.updateStatistics = function(oldXp, oldMoney) { const xpDiff = Character.experience - oldXp const moneyDiff = Character.money - oldMoney Vajda.statistics.sessionXpCount += xpDiff Vajda.statistics.totalXpCount += xpDiff if ( moneyDiff > 0 ) { //spending money while vajda is running would make this a bit funky Vajda.statistics.sessionMoneyCount += moneyDiff Vajda.statistics.totalMoneyCount += moneyDiff } } Vajda.run = async function() { Vajda.checkMotivation(0, [], async function(result) { if ( ( Vajda.isMotivationAbove(result) || Vajda.isStopMotivationZero()) && Character.energy > 0 && !Vajda.isHealthBelowLimit() ) { Vajda.currentState = 1 Vajda.selectTab("chosenJobs") Vajda.prepareJobRun(Vajda.currentJob.job) } else { if ( !Vajda.canAddMissing(result) ) { Vajda.finishRun() } else { let answer = await Vajda.fillUp(result) if ( !answer ) { Vajda.finishRun() } } } }) } Vajda.formatNumber = function(number) { if ( typeof number === 'number' ) { number = String(number) } const numberString = number.replace(/[ ,]/g, '') let formattedNumber = '' for ( let i = 0; i < numberString. length; i++ ) { if ( i > 0 && i % 3 === 0 ) { formattedNumber = ' ' + formattedNumber } formattedNumber = numberString[numberString.length - 1 - i] + formattedNumber } return formattedNumber } Vajda.createConsumablesTable = function() { let htmlSkel = $(`<div id='consumables_overview'></div>`) let html = $(` <div class ='consumables_filter' style='position: relative'> <div id='energy_consumables' style='position: absolute; top: 10px; left: 15px'></div> <div id='motivation_consumables' style='position: absolute; top: 10px; left: 160px'></div> <div id='health_consumables' style='position: absolute; top: 10px; left: 320px'></div> <div id='buff_consumables' style='position: absolute; top: 10px; left: 460px'></div> </div>` ) let table = new west.gui.Table() let consumableList = Vajda.filterConsumables(Vajda.consumableSelection.energy, Vajda.consumableSelection.motivation, Vajda.consumableSelection.health, Vajda.consumableSelection.hideBuffs) table.addColumn("consumIcon","consumIcon").addColumn("consumCount","consumCount").addColumn("consumEnergy","consumEnergy").addColumn("consumMotivation","consumMotivation").addColumn("consumHealth","consumHealth").addColumn("consumBuffs", "consumBuffs").addColumn("consumSelected","consumSelected") table.appendToCell("head","consumIcon","Image").appendToCell("head","consumCount","Count").appendToCell("head","consumEnergy","Energy").appendToCell("head","consumMotivation","Motivation").appendToCell("head","consumHealth","Health").appendToCell("head", "consumBuffs", "Buffs").appendToCell("head","consumSelected","Use") for ( const consumable of consumableList ) { const checkbox = new west.gui.Checkbox() checkbox.setSelected(consumable.isSelected) checkbox.setId(consumable.id) checkbox.setCallback(function() { Vajda.consumableTablePosition.content = $(".vajda-window .tw2gui_scrollpane_clipper_contentpane").css("top") Vajda.consumableTablePosition.scrollbar = $(".vajda-window .tw2gui_scrollbar_pulley").css("top") if ( consumable instanceof Buff && !consumable.hasCooldown() ) { Manager.selectedBuffs = consumable Vajda.selectTab('consumables') Vajda.setCookies() return } Vajda.changeConsumableSelection(parseInt(this.divMain.attr("id")), this.isSelected()) Vajda.selectTab("consumables") Vajda.setCookies() }) table.appendRow().appendToCell(-1,"consumIcon", Vajda.getConsumableIcon(consumable.image)).appendToCell(-1,"consumCount",consumable.count).appendToCell(-1,"consumEnergy",consumable.energy).appendToCell(-1,"consumMotivation",consumable.motivation).appendToCell(-1,"consumHealth",consumable.health).appendToCell(-1, "consumBuffs", consumable.getBuffHTML()).appendToCell(-1,"consumSelected",checkbox.getMainDiv()) } const buttonSelect = new west.gui.Button("Select all", function() { Vajda.changeSelectionAllConsumables(true) Vajda.selectTab("consumables") Vajda.setCookies() }) const buttonDeselect = new west.gui.Button("Deselect all", function() { Vajda.changeSelectionAllConsumables(false) Vajda.selectTab("consumables") Vajda.setCookies() }) table.appendToFooter("consumEnergy",buttonSelect.getMainDiv()) table.appendToFooter("consumHealth",buttonDeselect.getMainDiv()) htmlSkel.append(table.getMainDiv()) const checkboxEnergyConsumes = new west.gui.Checkbox() checkboxEnergyConsumes.setLabel("Energy consumables") checkboxEnergyConsumes.setSelected(Vajda.consumableSelection.energy) checkboxEnergyConsumes.setCallback(function() { Vajda.consumableSelection.energy = this.isSelected() Vajda.selectTab("consumables") }) const checkboxMotivationConsumes = new west.gui.Checkbox() checkboxMotivationConsumes.setLabel("Motivation consumables") checkboxMotivationConsumes.setSelected(this.consumableSelection.motivation) checkboxMotivationConsumes.setCallback(function() { Vajda.consumableSelection.motivation = this.isSelected() Vajda.selectTab("consumables") }) const checkboxHealthConsumes = new west.gui.Checkbox() checkboxHealthConsumes.setLabel("Health consumables") checkboxHealthConsumes.setSelected(this.consumableSelection.health) checkboxHealthConsumes.setCallback(function() { Vajda.consumableSelection.health = this.isSelected() Vajda.selectTab("consumables") }) const buffsFilter = new west.gui.Checkbox() buffsFilter.setLabel("Hide buffs") buffsFilter.setSelected(this.consumableSelection.hideBuffs) buffsFilter.setCallback(function() { Vajda.consumableSelection.hideBuffs = this.isSelected() Vajda.selectTab("consumables") }) $("#energy_consumables", html).append(checkboxEnergyConsumes.getMainDiv()) $("#motivation_consumables", html).append(checkboxMotivationConsumes.getMainDiv()) $("#health_consumables", html).append(checkboxHealthConsumes.getMainDiv()) $("#buff_consumables", html).append(buffsFilter.getMainDiv()) htmlSkel.append(html) return htmlSkel } Vajda.createAddedJobsTab = function() { const htmlSkel = $(`<div id='added_jobs_overview'></div>`) const footerHtml = $(` <div id='start_vajda' style='position: relative'> <span id='vajda-state-info' class='vajda_state' style='position: absolute; left: 20px; top: 10px; font-family: Arial, Helvetica, sans-serif; font-size: 15px; font-weight: bold;'> Current state: ${Vajda.states[Vajda.currentState]} </span> <div class='vajda_run' style='position: absolute; left: 350px; top: 20px'></div> </div> `) const table = new west.gui.Table() table.addColumn("jobIcon","jobIcon").addColumn("jobName","jobName").addColumn("jobStopMotivation","jobStopMotivation").addColumn("jobSet","jobSet").addColumn("jobRemove","jobRemove") table.appendToCell("head","jobIcon","Job icon").appendToCell("head","jobName","Job name").appendToCell("head","jobStopMotivation","Stop motivation").appendToCell("head","jobSet","Job set").appendToCell("head","jobRemove","") for ( let job = 0; job < Vajda.addedJobs.length; job++ ) { table.appendRow().appendToCell(-1,"jobIcon", Vajda.getJobIcon(Vajda.addedJobs[job].isSilver, Vajda.addedJobs[job].id, Vajda.addedJobs[job].x, Vajda.addedJobs[job].y)).appendToCell(-1,"jobName", Vajda.getJobName(Vajda.addedJobs[job].id)).appendToCell(-1,"jobStopMotivation", Vajda.createMinMotivationTextfield(Vajda.addedJobs[job].x, Vajda.addedJobs[job].y, Vajda.addedJobs[job].id, Vajda.addedJobs[job].stopMotivation)).appendToCell(-1,"jobSet", Vajda.createComboxJobSets(Vajda.addedJobs[job].x, Vajda.addedJobs[job].y, Vajda.addedJobs[job].id)).appendToCell(-1,"jobRemove", Vajda.createRemoveJobButton(Vajda.addedJobs[job].x, Vajda.addedJobs[job].y, Vajda.addedJobs[job].id)) } const buttonStart = new west.gui.Button("Start", async function() { const parseSuccesful = Vajda.parseStopMotivation() if ( parseSuccesful ) { Vajda.currentState = 3 $(`#vajda-state-info`).text(`Current state: ${Vajda.states[3]}`) Vajda.createRoute() Vajda.isRunning = true Vajda.setCookies() if ( Manager.isOptimized && Manager.hasEnoughPlugsAndDecorations() ) { await Manager.createSchedule() } Observer.start() Vajda.run() } else { new UserMessage("Wrong format of set stop motivation", UserMessage.TYPE_ERROR).show() } }); const buttonStop = new west.gui.Button("Stop",function() { Vajda.isRunning = false Vajda.currentState = 0 Vajda.selectTab("chosenJobs") Observer.stop() }) htmlSkel.append(table.getMainDiv()) $(".vajda_run",footerHtml).append(buttonStart.getMainDiv()) $(".vajda_run",footerHtml).append(buttonStop.getMainDiv()) htmlSkel.append(footerHtml) return htmlSkel } Vajda.createStatisticsGui = function() { const offsetLeft = '.5rem' const statsBubbleStyle = `' padding: 1rem 2rem; border-radius: 10px; background-color: rgba(255, 255, 228, .3); position: relative; box-shadow: 0 0 5px rgba(0, 0, 0, .2) '` const pseudoHeadingStyle = `' display: block; width: calc(100% - ${offsetLeft}); padding-left: ${offsetLeft}; border-bottom: 1px solid black; margin-bottom: .5rem '` const refreshStats = ` <div style=${statsBubbleStyle}> <b style=${pseudoHeadingStyle}>REFRESH COUNT</b> <p style='padding-left:${offsetLeft}'>Total in this session: ${Observer.refreshCount}</p> </div> ` return $(` <div id='statistics_overview' style='padding: 0 2rem; display: grid; gap: 1rem'> <div style=${statsBubbleStyle}> <b style=${pseudoHeadingStyle}>EXPERIENCE</b> <p style='padding-left: ${offsetLeft}'>Total in this session: ${Vajda.formatNumber(Vajda.statistics.sessionXpCount)}</p> <p style='padding-left: ${offsetLeft}'>Total overall: ${Vajda.formatNumber(Vajda.statistics.totalXpCount)}</p> </div> <div style=${statsBubbleStyle}> <b style=${pseudoHeadingStyle}>MONEY</b> <p style='padding-left: ${offsetLeft}'>Total in this session: ${Vajda.formatNumber(Vajda.statistics.sessionMoneyCount)}</p> <p style='padding-left: ${offsetLeft}'>Total overall: ${Vajda.formatNumber(Vajda.statistics.totalMoneyCount)}</p> </div> <div style=${statsBubbleStyle}> <b style=${pseudoHeadingStyle}>JOBS</b> <p style='padding-left: ${offsetLeft}'>Total in this session: ${Vajda.formatNumber(Vajda.statistics.sessionJobsCount)}</p> <p style='padding-left: ${offsetLeft}'>Total overall: ${Vajda.formatNumber(Vajda.statistics.totalJobsCount)}</p> </div> ${Observer.isEnabled ? refreshStats : ''} </div> `) } Vajda.createSettingsGui = function() { const htmlSkel = $(`<div id='settings_overview' style='padding: 10px'></div>`) const checkboxAddEnergy = new west.gui.Checkbox() checkboxAddEnergy.setLabel("Add energy") checkboxAddEnergy.setSelected(Vajda.settings.addEnergy) checkboxAddEnergy.setCallback(function() { Vajda.settings.addEnergy = !Vajda.settings.addEnergy }) const checkboxAddMotivation = new west.gui.Checkbox() checkboxAddMotivation.setLabel("Add motivation") checkboxAddMotivation.setSelected(Vajda.settings.addMotivation) checkboxAddMotivation.setCallback(function() { Vajda.settings.addMotivation = !Vajda.settings.addMotivation }) const checkboxAddHealth = new west.gui.Checkbox() checkboxAddHealth.setLabel("Add health") checkboxAddHealth.setSelected(Vajda.settings.addHealth) checkboxAddHealth.setCallback(function() { Vajda.settings.addHealth = !Vajda.settings.addHealth }) const buttPlugsCheckbox = new west.gui.Checkbox() buttPlugsCheckbox.setLabel('Optimize for butt plugs and cake decorations') buttPlugsCheckbox.setSelected(Manager.isOptimized) buttPlugsCheckbox.setCallback(() => { Manager.isOptimized = p => !p }) const enableObserverCheckbox = new west.gui.Checkbox() enableObserverCheckbox.setLabel('[Experimental] Enable auto refresh → Read user manual') enableObserverCheckbox.setSelected(Observer.isEnabled) enableObserverCheckbox.setCallback(() => { Observer.isEnabled = previous => { $('#observer-delay-input').css('max-height', previous ? '0px' : '30px') Vajda.isRunning && !previous && Observer.start(forceStart = true) Vajda.isRunning && previous && Observer.stop() return !previous } }) const observerDelayInput = new west.gui.Textfield() observerDelayInput.setWidth(100) observerDelayInput.setValue(Observer.getTimeOut(true)) observerDelayInput.getMainDiv()[0].querySelector('input').addEventListener('change', e => { const val = e.target.value if ( Vajda.isNumber(val) ) { Observer.timeOut = val } }) const style = (isOpen) => `' overflow: hidden; max-height: ${isOpen ? '30' : '0'}px; transition: max-height .5s; margin-bottom: 1rem; '` const observerDelay = $(` <div style=${style(Observer.isEnabled)} id='observer-delay-input'> Refresh page after </div> `) observerDelay.append(observerDelayInput.getMainDiv()) observerDelay.append(` minutes since the last action`) const saveMoneyCheckbox = new west.gui.Checkbox() saveMoneyCheckbox.setLabel('Deposit money in your hometown') saveMoneyCheckbox.setSelected(Vajda.settings.addDeposit.isEnabled) saveMoneyCheckbox.setCallback(() => { if ( Character.homeTown.town_id === 0 ) { new UserMessage("You don' have a home town", UserMessage.TYPE_HINT).show() return } const oldVal = !!Vajda.settings.addDeposit.isEnabled Vajda.settings.addDeposit.isEnabled = !oldVal $(`#deposit-limit-input`).css('max-height', oldVal ? '0px' : '30px') }) const depositLimit = $(` <div style=${style(Vajda.settings.addDeposit.isEnabled)} id='deposit-limit-input'> Deposit when more than </div> `) const limitInput = new west.gui.Textfield() limitInput.setWidth(100) limitInput.setValue(Number.isNaN(Vajda.settings.addDeposit.limit) ? '' : Vajda.settings.addDeposit.limit) limitInput.getMainDiv()[0].querySelector('input').addEventListener('change', e => { const val = e.target.value if ( Vajda.isNumber(val) ) { Vajda.settings.addDeposit.limit = val } }) depositLimit.append(limitInput.getMainDiv()) depositLimit.append(` $ in cash`) const htmlHealthStop = $("<div></div>") htmlHealthStop.append(`<span>Stoppage health percent value</span>`) const healthStopTextfiled = new west.gui.Textfield("healthStop") healthStopTextfiled.setValue(Vajda.settings.healthStop) healthStopTextfiled.setWidth(100) htmlHealthStop.append(healthStopTextfiled.getMainDiv()) const htmlSetWearDelay = $("<div></div>") htmlSetWearDelay.append("<span> Job set equip delay </span>") const setWearDelayTextfiled = new west.gui.Textfield("setWearDelay") setWearDelayTextfiled.setValue(Vajda.settings.setWearDelay) setWearDelayTextfiled.setWidth(100) htmlSetWearDelay.append(setWearDelayTextfiled.getMainDiv()) const htmlJobDelay = $("<div></div>") htmlJobDelay.append(`<span>Random delay between jobs(seconds)</span>`) const jobDelayTextFieldMin = new west.gui.Textfield("jobDelay") jobDelayTextFieldMin.setValue(Vajda.settings.jobDelayMin) jobDelayTextFieldMin.setWidth(50) const jobDelayTextFieldMax = new west.gui.Textfield("jobDelay") jobDelayTextFieldMax.setValue(Vajda.settings.jobDelayMax) jobDelayTextFieldMax.setWidth(50) htmlJobDelay.append(jobDelayTextFieldMin.getMainDiv()) htmlJobDelay.append("<span> - </span>") htmlJobDelay.append(jobDelayTextFieldMax.getMainDiv()) const manualLink = ` <div onclick=Vajda.selectTab('manual') style="cursor: pointer; text-decoration: underline; position: absolute; top: 20px; right: 25px;"> Open User Manual <svg style="width: 10px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V32c0-17.7-14.3-32-32-32H352zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"/></svg> </div> ` const buttonApply = new west.gui.Button("Apply", function() { Vajda.settings.addEnergy = checkboxAddEnergy.isSelected() Vajda.settings.addMotivation = checkboxAddMotivation.isSelected() Vajda.settings.addHealth = checkboxAddHealth.isSelected() if ( Vajda.isNumber(healthStopTextfiled.getValue()) ) { let healthStop = parseInt(healthStopTextfiled.getValue()) healthStop = Math.min(30,healthStop) Vajda.settings.healthStop = healthStop } if ( Vajda.isNumber(setWearDelayTextfiled.getValue()) ) { let setWearDelay = parseInt(setWearDelayTextfiled.getValue()) setWearDelay = Math.min(10,setWearDelay) Vajda.settings.setWearDelay = setWearDelay } if ( Vajda.isNumber(jobDelayTextFieldMin.getValue()) ) { let jobDelayTimeMin = parseInt(jobDelayTextFieldMin.getValue()) Vajda.settings.jobDelayMin = jobDelayTimeMin } else { Vajda.settings.jobDelayMin = 0 Vajda.settings.jobDelayMax = 0 new UserMessage("Wrong format of delay job min value. Please set a number.", UserMessage.TYPE_ERROR).show() } if ( Vajda.isNumber(jobDelayTextFieldMax.getValue()) ) { let jobDelayTimeMax = parseInt(jobDelayTextFieldMax.getValue()) Vajda.settings.jobDelayMax = jobDelayTimeMax } else { Vajda.settings.jobDelayMin = 0 Vajda.settings.jobDelayMax = 0 new UserMessage("Wrong format of delay job max value. Please set a number.", UserMessage.TYPE_ERROR).show() } Vajda.setCookies() Vajda.selectTab("settings") }) htmlSkel.append(manualLink) htmlSkel.append(checkboxAddEnergy.getMainDiv()) htmlSkel.append("<br>") htmlSkel.append(checkboxAddMotivation.getMainDiv()) htmlSkel.append("<br>") htmlSkel.append(checkboxAddHealth.getMainDiv()) htmlSkel.append("<br>") htmlSkel.append(buttPlugsCheckbox.getMainDiv()) htmlSkel.append("<br>") htmlSkel.append(enableObserverCheckbox.getMainDiv()) htmlSkel.append("<br>") htmlSkel.append(observerDelay) htmlSkel.append(saveMoneyCheckbox.getMainDiv()) htmlSkel.append(depositLimit) htmlSkel.append(htmlHealthStop) htmlSkel.append("<br>") htmlSkel.append(htmlSetWearDelay) htmlSkel.append("<br>") htmlSkel.append(htmlJobDelay) htmlSkel.append("<br>") htmlSkel.append(buttonApply.getMainDiv()) return htmlSkel } Vajda.createManualGui = function() { const listStyle = `'padding-inline-start: 14px'` const nestedListStyle = `'padding-inline-start: 30px'` const containerStyle = `' padding: 0 30px 30px '` const html = ` <div style=${containerStyle}> <h3>Read Before use</h3> <ol style=${listStyle}> <li> <strong>Calculating optimal route</strong> takes time and memory, a lot of it. It might not be noticeable for up to 12-14 jobs, but anything more than that... you will notice a short freeze of the window. I don't recommend selecting more than 18 jobs, as the algorithm will take about a minute (depending on the hardware, of course) to execute, while it takes almost 8 minutes to find the best route for 20 jobs. In these scenarios, your browser may appear unresponsive and prompt you to reload the page or wait. I recommend being patient and allowing some time for the algorithm to complete. If that doesn't work, remove one or two jobs; that's the cheapest way to solve this problem. </li> <li> <strong>The optimization for butt plugs and cake decorations</strong> setting, which is most likely why you are here: <ul style=${nestedListStyle}> <li>The setting has to be enabled <i>before</i> you start Zdenka, it won't work otherwise</li> <li>The <i>Add motivation</i> setting has to be enabled as well</li> <li>Do not start Zdenka with low energy and consumables on cooldown</li> <li>Do not use consumables yourself while it's running</li> <li> You don't have to select consumables manually with this setting, Zdenka will do it for you. Just make sure you have enough of them butt plugs and decorations </li> </ul> These restrictions are simply the cost of having Zdenka refill energy <i>while</i> doing jobs. </li> <li> Zdenka can <strong>travel</strong> to jobs that you can't do in travel set (unless you are super low level). </li> <li> <strong>Buffs</strong> will be used automatically when selected (and when no buff is active, of course). Only one buff of each type can be selected at once. Keep in mind that consumables with cooldown are <b>not</b> treated as buffs and will only be used to refill whatever your character needs. </li> <li> Zdenka will display <strong>all silver jobs</strong>, not just those you can do and are closest to yoor character. This allows you <i>some</i> control of the route, for instance there might be multiple silver jobs of the same kind and one of them might simply be in a more advantageous position. </li> <li> When <strong>Auto Refresh</strong> is enabled, Zdenka will refresh the page and start automatically <i>n</i> minutes after starting a job (or walking to one). Starting new jobs will restart this countdown. Do not set the delay to low numbers, Zdenka enforces a minimum of 2.5 minutes anyway, however, I do recommend a slightly longer delay. </li> </ol> </div> ` const container = new west.gui.Scrollpane().getMainDiv() container.style = 'height: 400px; overflow-y: scroll' container.innerHTML = html return container } Vajda.createMenuIcon = function() { const menuImage = 'data:image/jpeg;base64,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' let div = $('<div class="ui_menucontainer" />') let link = $('<div id="vajda_menu" class="menulink" onclick=Vajda.loadJobs() title="Zdenka Studenková" />').css('background-image', 'url(' + menuImage + ')') $('#ui_menubar').append((div).append(link).append('<div class="menucontainer_bottom" />')) } Vajda.createWindow = function(isHumanAction = true) { const window = wman.open("vajda").setResizeable(false).setMinSize(650, 480).setSize(650, 480).setMiniTitle("Vajda Jožo") const tabs = { "jobs": "Jobs", "chosenJobs": "Chosen Jobs", "sets": "Sets", "consumables": "Consumables", "stats": "Statistics", "settings": "Settings", "manual": "User manual" } let tabLogic = function(win,id) { const content = $(`<div class='vajda-window'></div>`) switch(id) { case 'jobs': Vajda.loadJobData(function(){ Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("jobs",this) content.append(Vajda.createJobsTab()) Vajda.window.appendToContentPane(content) Vajda.addJobTableCss() $(".vajda-window .tw2gui_scrollpane_clipper_contentpane").css({"top": Vajda.jobTablePosition.content}) $(".vajda-window .tw2gui_scrollbar_pulley").css({"top": Vajda.jobTablePosition.scrollbar}) Vajda.addEventsHeader() }) break case 'chosenJobs': Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("chosenJobs", this) content.append(Vajda.createAddedJobsTab()) Vajda.window.appendToContentPane(content) $(".vajda-window .tw2gui_scrollpane_clipper_contentpane").css({"top": Vajda.addedJobTablePosition.content}) $(".vajda-window .tw2gui_scrollbar_pulley").css({"top": Vajda.addedJobTablePosition.scrollbar}) Vajda.addAddedJobsTableCss() break case 'sets': Vajda.loadSets(function() { Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("sets",this) content.append(Vajda.createSetGui()) Vajda.window.appendToContentPane(content) }) break case 'consumables': Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("consumables",this) Vajda.findAllConsumables() content.append(Vajda.createConsumablesTable()) Vajda.window.appendToContentPane(content) $(".vajda-window .tw2gui_scrollpane_clipper_contentpane").css({"top": Vajda.consumableTablePosition.content}) $(".vajda-window .tw2gui_scrollbar_pulley").css({"top": Vajda.consumableTablePosition.scrollbar}) Vajda.addConsumableTableCss() break case 'stats': Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("stats",this) content.append(Vajda.createStatisticsGui()) Vajda.window.appendToContentPane(content) break case 'settings': Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("settings",this) content.append(Vajda.createSettingsGui()) Vajda.window.appendToContentPane(content) break case 'manual': Vajda.removeActiveTab(this) Vajda.removeWindowContent() Vajda.addActiveTab("manual", this) content.append(Vajda.createManualGui()) Vajda.window.appendToContentPane(content) break } } for(let tab in tabs) { window.addTab(tabs[tab],tab,tabLogic) } Vajda.window = window if ( !isHumanAction ) wman.close('vajda') Vajda.selectTab('jobs') } Vajda.selectTab = function(key) { Vajda.window.tabIds[key].f(Vajda.window,key) } Vajda.removeActiveTab = function(window) { $('div.tw2gui_window_tab', window.divMain).removeClass('tw2gui_window_tab_active') } Vajda.addActiveTab = function(key, window) { $(`div._tab_id_${key}`, window.divMain).addClass('tw2gui_window_tab_active') } Vajda.removeWindowContent = function() { $(".vajda-window").remove() } Vajda.addConsumableTableCss = function() { $(".vajda-window .consumIcon").css({"width":"80px"}) $(".vajda-window .consumCount").css({"width":"60px"}) $(".vajda-window .consumEnergy").css({"width":"60px"}) $(".vajda-window .consumMotivation").css({"width":"70px"}) $(".vajda-window .consumHealth").css({"width":"60px"}) $(".vajda-window .consumBuffs").css({"width": "150px"}) $(".vajda-window .row").css({"height":"80px"}) $('.vajda-window').find('.tw2gui_scrollpane').css('height', '250px') } Vajda.addJobTableCss = function() { $(".vajda-window .jobIcon").css({"width":"80px"}) $(".vajda-window .jobName").css({"width":"150px"}) $(".vajda-window .jobXp").css({"width":"40px"}) $(".vajda-window .jobMoney").css({"width":"40px"}) $(".vajda-window .jobMotivation").css({"width":"40px"}) $(".vajda-window .jobDistance").css({"width":"100px"}) $(".vajda-window .row").css({"height":"60px"}) $('.vajda-window').find('.tw2gui_scrollpane').css('height', '250px') } Vajda.addAddedJobsTableCss = function() { $(".vajda-window .jobIcon").css({"width":"80px"}) $(".vajda-window .jobName").css({"width":"130px"}) $(".vajda-window .jobStopMotivation").css({"width":"110px"}) $(".vajda-window .jobRemove").css({"width":"105px"}) $(".vajda-window .jobSet").css({"width":"100px"}) $(".vajda-window .row").css({"height":"60px"}) $('.vajda-window').find('.tw2gui_scrollpane').css('height', '250px') } Vajda.createJobsTab = function() { const htmlSkel = $(`<div id='jobs_overview'></div>`) const html = $(` <div class='jobs_search' style='position: relative'> <div id='jobFilter' style='position: absolute; top: 10px; left: 15px'></div> <div id='job_only_silver' style='position: absolute; top:10px; left: 200px;'></div> <div id='job_no_silver' style='position: absolute; top: 10px; left: 270px;'></div> <div id='job_center' style='position: absolute; top: 10px; left: 350px;'></div> <div id='button_filter_jobs' style='position: absolute; top: 5px; left: 450px;'></div> </div> `) const table = new west.gui.Table() const xpIcon = '<img src="/images/icons/star.png">' const dollarIcon = '<img src="/images/icons/dollar.png">' const motivationIcon = '<img src="/images/icons/motivation.png">' const arrow_desc = ' <img src="../images/window/jobs/sortarrow_desc.png"/>' const arrow_asc = ' <img src="../images/window/jobs/sortarrow_asc.png"/>' const uniqueJobs = Vajda.getAllUniqueJobs() table .addColumn("jobIcon","jobIcon") .addColumn("jobName","jobName") .addColumn("jobXp","jobXp") .addColumn("jobMoney","jobMoney") .addColumn("jobMotivation","jobMotivation") .addColumn("jobDistance","jobDistance") .addColumn("jobAdd","jobAdd") table.appendToCell("head","jobIcon","Job icon").appendToCell("head","jobName","Job name").appendToCell("head","jobXp",xpIcon + (Vajda.sortJobTableXp == 1 ? arrow_asc : Vajda.sortJobTableXp == -1 ? arrow_desc : "")).appendToCell("head","jobMoney",dollarIcon).appendToCell("head","jobMotivation",motivationIcon).appendToCell("head","jobDistance","Distance " + (Vajda.sortJobTableDistance == 1 ? arrow_asc : Vajda.sortJobTableDistance == -1 ? arrow_desc : "")).appendToCell("head","jobAdd","") for ( let job = 0; job < uniqueJobs.length; job++ ) { table .appendRow() .appendToCell(-1,"jobIcon",Vajda.getJobIcon(uniqueJobs[job].isSilver,uniqueJobs[job].id,uniqueJobs[job].x,uniqueJobs[job].y)) .appendToCell(-1,"jobName",Vajda.getJobName(uniqueJobs[job].id)) .appendToCell(-1,"jobXp",uniqueJobs[job].experience) .appendToCell(-1,"jobMoney",uniqueJobs[job].money) .appendToCell(-1,"jobMotivation",uniqueJobs[job].motivation) .appendToCell(-1,"jobDistance",uniqueJobs[job].distance.formatDuration()) .appendToCell(-1,"jobAdd", Vajda.createAddJobButton(uniqueJobs[job].x,uniqueJobs[job].y,uniqueJobs[job].id)) } const textfield = new west.gui.Textfield("jobsearch").setPlaceholder("Select job name") if ( Vajda.jobFilter.filterJob !== "" ) { textfield.setValue(Vajda.jobFilter.filterJob) } const checkboxOnlySilver = new west.gui.Checkbox() checkboxOnlySilver.setLabel("Silvers") checkboxOnlySilver.setSelected(Vajda.jobFilter.filterOnlySilver) checkboxOnlySilver.setCallback(function() { if ( this.isSelected() ) { Vajda.jobFilter.filterOnlySilver = true }else { Vajda.jobFilter.filterOnlySilver = false } }) const checkboxNoSilver = new west.gui.Checkbox() checkboxNoSilver.setLabel("No silvers") checkboxNoSilver.setSelected(Vajda.jobFilter.filterNoSilver) checkboxNoSilver.setCallback(function() { if ( this.isSelected() ) { Vajda.jobFilter.filterNoSilver = true } else { Vajda.jobFilter.filterNoSilver = false } }) const checkboxCenterJobs = new west.gui.Checkbox() checkboxCenterJobs.setLabel("Center jobs") checkboxCenterJobs.setSelected(Vajda.jobFilter.filterCenterJobs) checkboxCenterJobs.setCallback(function() { if ( this.isSelected() ) { Vajda.jobFilter.filterCenterJobs = true } else { Vajda.jobFilter.filterCenterJobs = false } }) const buttonFilter = new west.gui.Button("Filter", function() { Vajda.jobFilter.filterJob = textfield.getValue() Vajda.jobTablePosition.content = "0px" Vajda.jobTablePosition.scrollbar = "0px" Vajda.selectTab("jobs") }) htmlSkel.append(table.getMainDiv()) $('#jobFilter', html).append(textfield.getMainDiv()) $("#job_only_silver",html).append(checkboxOnlySilver.getMainDiv()) $("#job_no_silver",html).append(checkboxNoSilver.getMainDiv()) $("#job_center",html).append(checkboxCenterJobs.getMainDiv()) $("#button_filter_jobs",html).append(buttonFilter.getMainDiv()) htmlSkel.append(html) return htmlSkel } Vajda.createAddJobButton = function(x, y, id) { const buttonAdd = new west.gui.Button("Add new job", function() { Vajda.addJob(x, y, id) Vajda.jobTablePosition.content = $(".vajda-window .tw2gui_scrollpane_clipper_contentpane").css("top") Vajda.jobTablePosition.scrollbar = $(".vajda-window .tw2gui_scrollbar_pulley").css("top") Vajda.selectTab("jobs") }) buttonAdd.setWidth(100) return buttonAdd.getMainDiv() } Vajda.createMinMotivationTextfield = function(x, y, id, placeholder) { const componentId = `x-${x}y-${y}id-${id}` const textfield = new west.gui.Textfield() textfield.setId(componentId) textfield.setWidth(40) textfield.setValue(placeholder) return textfield.getMainDiv() } Vajda.createRemoveJobButton = function(x, y, id) { const buttonRemove = new west.gui.Button("Remove job", function() { Vajda.removeJob(x, y, id) Vajda.addedJobTablePosition.content = $(".vajda-window .tw2gui_scrollpane_clipper_contentpane").css("top") Vajda.addedJobTablePosition.scrollbar = $(".vajda-window .tw2gui_scrollbar_pulley").css("top") Vajda.selectTab("chosenJobs") }) buttonRemove.setWidth(100) return buttonRemove.getMainDiv() } $(document).ready(() => { try { Vajda.loadLanguage() Vajda.loadSets() Vajda.createMenuIcon() Vajda.getCookies() Observer.resumeSession() } catch(e) { console.log(e) console.log("exception occured") } }) })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址