Skip to content

Instantly share code, notes, and snippets.

@YoukaiCat
Last active November 11, 2022 01:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YoukaiCat/e6eab1eae89a5a035ef2 to your computer and use it in GitHub Desktop.
Save YoukaiCat/e6eab1eae89a5a035ef2 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name HentaiVerseBot
// @namespace YoukaiCat
// @description HentaiVerse Bot
// @include http://hentaiverse.org/*
// @version 0.0.1
// @grant none
// @require http://code.jquery.com/jquery-2.1.4.min.js
// ==/UserScript==
"use strict";
let HentaiVerseBot = () => {
let log = (object) => console.log(object.toString());
//Случайное число в промежутке [min...max]
let randInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
class Stat {
constructor(name, amount) {
this.name = name;
this.amount = amount;
}
toString() {
return `${this.name}: ${this.amount}`;
}
}
class StatCreator {
//Здровье, мана, дух и оверчардж
static simpleStatFrom(object) {
const FullWidth = 120;
let alt = object.attr("alt");
let style = object.attr("style");
let match = /\w\:(\d{0,3})px/.exec(style);
let width = parseInt(match[1]);
let percentage = width * 100 / FullWidth; //.find("~ .cwbt > .cwbt1 > .fd2 > div")
return new Stat(alt, percentage);
}
static staminaStatFrom(object) {
let text = object.text();
let match = /Stamina:\s*(\d{0,3})/.exec(text);
let persentage = parseInt(match[1]);
return new Stat("stamina", persentage);
}
}
class ClickableObject {
constructor(object) {
this.object = object;
}
click() {
let sleep = (milliseconds) => {
let currentTime = () => new Date().getTime();
let start = currentTime();
while (true) {
if ((currentTime() - start) > milliseconds) {
break;
}
}
};
let timeout = randInRange(800, 3000);
// sleep(timeout); //Мы не быстрые
this.object.hover(); //Навести курсор, вдруг анти-бот анализирует?
// sleep(300);
this.object.click(); //DOIT
// sleep(10000); //Заморозим выполнение, пока страничка не перезагрузилась
}
}
class Ability extends ClickableObject {
constructor(object, name, cost, cooldown) {
super(object);
this.name = name;
this.cost = cost;
this.cooldown = cooldown;
}
toString() {
return `${this.name}, ${this.cost}, ${this.cooldown}`;
}
}
class Spell extends Ability {
constructor(object, name, cost, cooldown) {
super(object, name, cost, cooldown)
}
}
class WeaponSkill extends Ability {
constructor(object, name, cost, cooldown) {
super(object, name, cost, cooldown)
}
}
class InnateSkill extends Ability {
constructor(object, name) {
super(object, name, 0, 0)
}
}
class AbilityCreator {
static abilityFrom(object) {
let onmouseover = object.attr("onmouseover");
let match = /battle\.set\_infopane\_spell\(\'([^']+)\'\, \'[^']*\'\, \'[^']*\'\, (\d+)\, (\d+)\, (\d+)\)/.exec(onmouseover);
let name = match[1];
let magicCost = parseInt(match[2]);
let overchargeCost = parseInt(match[3]);
let cooldown = parseInt(match[4]);
if (magicCost === 0 && overchargeCost === 0) {
return new InnateSkill(object, name);
} else if (magicCost === 0 && overchargeCost > 0) {
return new WeaponSkill(object, name, overchargeCost, cooldown);
} else if (magicCost > 0 && overchargeCost === 0) {
return new Spell(object, name, magicCost, cooldown)
} else {
console.log("Unknown ability:" + name);
}
}
}
class Effect {
constructor(name, duration) {
this.name = name;
this.duration = duration;
}
toString() {
return `${this.name}, ${this.duration}`;
}
}
class EffectCreator {
static effectFrom(object) {
let onmouseover = object.attr("onmouseover");
let match = /battle\.set\_infopane\_effect\(\'([^']+)\'\, \'[^']*\'\, (\d+)\)/.exec(onmouseover);
let name = match[1];
let duration = parseInt(match[2]);
return new Effect(name, duration);
}
}
class Item extends ClickableObject {
constructor(object, name) {
super(object);
this.name = name;
}
toString() {
return `${this.name}`;
}
}
class ItemCreator {
static itemFrom(object) {
let onmouseover = object.attr("onmouseover");
let match = /battle\.set\_infopane\_item\(\'([^']+)\'\,\'[^']*\'\,\'[^']*\'\)/.exec(onmouseover);
let name = match[1];
return new Item(object, name);
}
}
class Enemy extends ClickableObject {
constructor(object, name, level, stats, effects) {
super(object);
this.name = name;
this.level = level;
this.stats = stats;
this.effects = effects;
}
toString() {
return `${this.name}, ${this.level}, ${this.stats}, ${this.effects}`;
}
}
class Boss extends Enemy {
constructor(object, name, level, stats, effects, bossTier) {
super(object, name, level, stats, effects);
this.bossTier = bossTier;
}
}
class EnemyCreator {
static enemyFrom(object) {
let name = $(object.find(".btm3 > .fd2 > div")[0]).text();
let level = $(object.find(".btm2 > div > .fd4 > div")[0]).text();
let stats = jQuery.makeArray(object.find(".chbd > img[class=chb2]")).map(x => StatCreator.simpleStatFrom($(x)));
let effects = jQuery.makeArray(object.find(".btm6 > img")).map(x => EffectCreator.effectFrom($(x)));
let style = object.attr("style");
let match = /border\:\d+px ridge (\#[\d\w]+)/.exec(style);
let color = match[1];
// Боссы отличаются по цвету
if (color === "#5C0D12") {
return new Enemy(object, name, level, stats, effects);
} else {
return new Boss(object, name, level, stats, effects, 0);
}
}
}
class PlayerProperty {
constructor(objects) {
objects.reduce((x, y) => Object.defineProperty(x, y.name, { value: y }), this);
}
has(name) {
return this.hasOwnProperty(name);
}
use(name) {
return this[name].click();
}
useIfAvailable(name) {
if (this.has(name)) {
this.use(name);
return true;
} else {
return false;
}
}
}
class Player {
constructor(stats, abilities, effects, items, enemies) {
this.stats = new PlayerProperty(stats);
this.abilities = new PlayerProperty(abilities);
this.effects = new PlayerProperty(effects);
this.items = new PlayerProperty(items);
this.enemies = enemies;
this.stopped = false;
}
notify(message) {
new Notification(message);
}
notifyAndStop(message) {
this.stopped = true;
this.notify(message);
}
checkBosses() {
let isBoss = (enemy) => enemy instanceof Boss;
if (this.enemies.some(isBoss)) {
this.notifyAndStop("ХОЗЯИН, ПОМОГИ, БОСС!!");
}
}
checkStamina() {
if (this.stats.stamina.amount < 80) {
this.notify("НЕЭФФЕКТИВНАЯ ПРОКАЧКА");
}
}
useFirstAvailable(entitiesNamePairs) {
for (let pair of entitiesNamePairs) {
if (pair[0].useIfAvailable(pair[1])) {
return true;
}
}
return false;
}
checkHealth() {
if (this.stats.health.amount < 40) {
let alternatives = [[this.items, "Mystic Gem"], [this.items, "Mana Gem"], [this.items, "Health Gem"],
[this.abilities, "Cure"], [this.items, "Mana Potion"], [this.items, "Health Potion"]];
if (!this.useFirstAvailable(alternatives)) {
this.notifyAndStop("НЕЧЕМ ПОПОЛНИТЬ ХП!");
}
}
}
checkMagic() {
if (this.stats.magic.amount < 35) {
let alternatives = [[this.items, "Mana Gem"], [this.items, "Mana Potion"]];
if (!this.useFirstAvailable(alternatives)) {
this.notifyAndStop("НЕЧЕМ ПОПОЛНИТЬ МАНУ!");
}
}
}
checkBuff(name) {
let effectItemPairs = {
"Replenishment": "Mana Draught",
"Regeneration": "Health Draught"
};
let effectSpellPairs = {
"Hastened": "Haste"
};
if (!this.effects.has(name)) {
if (effectItemPairs.hasOwnProperty(name)) {
if (!this.items.useIfAvailable(effectItemPairs[name])) {
this.notifyAndStop(`НЕТ ИТЕМА: ${name}!`);
}
} else {
if (!((effectSpellPairs.hasOwnProperty(name) &&
this.abilities.useIfAvailable(effectSpellPairs[name])
) || this.abilities.useIfAvailable(name))) {
this.notifyAndStop(`НЕ КАСТУЕТСЯ БАФФ: ${name}`);
}
}
}
}
checkExpensiveBuff(name) {
if (!this.effects.has(name)) {
if (this.effects.has("Channeling")) {
this.abilities.useIfAvailable(name);
} else {
this.items.useIfAvailable("Mystic Gem");
}
}
}
checkBuffs() {
this.checkExpensiveBuff("Heartseeker");
["Replenishment", "Regeneration", "Regen", "Protection",
"Shadow Veil", "Hastened"].forEach(x => { if (!this.stopped) this.checkBuff(x) });
}
hitEnemy() {
if (this.enemies.length > 0) {
let n = randInRange(0, this.enemies.length - 1);
let enemy = this.enemies[n];
if (this.stats.overcharge.amount > 98) {
if (this.effects.has("Chain 2") && this.abilities.useIfAvailable("Frenzied Blows")) {
enemy.click();
} else if (this.effects.has("Chain 1") && this.abilities.useIfAvailable("Backstab")) {
enemy.click();
} else if (this.abilities.useIfAvailable("Iris Strike")) {
enemy.click();
}
} else {
enemy.click();
}
} else {
this.notifyAndStop("НЕТ МОНСТРОВ, НЕКОГО БИТЬ!");
}
}
fight() {
//TODO: check Riddlemaster
let newRound = $("#ckey_continue");
if (newRound.length > 0) {
newRound.click();
} else {
[this.checkBosses, this.checkStamina, this.checkHealth, this.checkMagic,
this.checkBuffs, this.hitEnemy].forEach(f => { if (!this.stopped) f.call(this) });
}
}
}
class HentaiVerse {
static getSimpleStats() {
let elements = jQuery.makeArray($(".cwb2"));
return elements.map(x => StatCreator.simpleStatFrom($(x)));
}
static getStaminaStat() {
return StatCreator.staminaStatFrom($($("div:contains('Stamina:')")[2]).find("div"));
}
static getStats() {
return HentaiVerse.getSimpleStats().concat(HentaiVerse.getStaminaStat());
}
static getAbilities() {
let elements = jQuery.makeArray($(".bts > div"));
return elements
.map(x => $(x))
.filter(x => x.attr("onclick") !== undefined)
.map(x => AbilityCreator.abilityFrom(x));
}
static getEffects() {
let elements = jQuery.makeArray($(".bte > img"));
return elements.map(x => EffectCreator.effectFrom($(x)));
}
static getItems() {
let elements = jQuery.makeArray($(".bti3 > div"));
return elements
.map(x => $(x))
.filter(x => x.attr("onclick") !== undefined)
.map(x => ItemCreator.itemFrom(x));
}
static getMonsters() {
let elements = jQuery.makeArray($("#monsterpane > div"));
return elements
.map(x => $(x))
.filter(x => x.attr("onclick") !== undefined)
.map(x => EnemyCreator.enemyFrom(x));
}
static main() {
let player = new Player(HentaiVerse.getStats(), HentaiVerse.getAbilities(),
HentaiVerse.getEffects(), HentaiVerse.getItems(), HentaiVerse.getMonsters());
setTimeout(() => player.fight(), randInRange(800, 3000));
}
}
return HentaiVerse;
};
$(document).ready(() => HentaiVerseBot().main()); //ruby -run -ehttpd . -p8000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment