Silver-Pexer 2

XP automatique sur le jeu Silver-World

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

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

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

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

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

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

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

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         Silver-Pexer 2
// @namespace    https://silver-world.net/?silver-pexer
// @version      1.3
// @description  XP automatique sur le jeu Silver-World
// @author       Goodq
// @match        https://silver-world.net/*
// @icon         https://www.google.com/s2/favicons?domain=silver-world.net
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

"use strict";

// src/utils/doThenWait.ts
function doThenWait(action, until) {
  return new Promise((resolve, reject) => {
    try {
      action();
    } catch (e) {
      reject(e);
    }
    const interval = setInterval(() => {
      if (until()) {
        clearInterval(interval);
        resolve();
      }
    }, config.waitInterval);
    setTimeout(() => {
      clearInterval(interval);
      reject(new Error("waitUntilTextUpdated timeout"));
    }, config.waitTimeout);
  });
}

// src/utils/silverPexerSettings.ts
var Skills = /* @__PURE__ */ ((Skills2) => {
  Skills2["constitution"] = "constitution";
  Skills2["force"] = "force";
  Skills2["agilite"] = "agilite";
  Skills2["intelligence"] = "intelligence";
  return Skills2;
})(Skills || {});
var Shortcuts = {
  first: "0",
  second: "1",
  third: "2"
};
var SILVER_PEXER_SETTINGS_KEY = "silver-pexer-settings";
function setItem(key, value) {
  const settings = JSON.parse(localStorage.getItem(SILVER_PEXER_SETTINGS_KEY) ?? "{}");
  const newSettings = { ...settings };
  newSettings[key] = value;
  localStorage.setItem(SILVER_PEXER_SETTINGS_KEY, JSON.stringify(newSettings));
}
function getItem(key) {
  const existingSettings = JSON.parse(localStorage.getItem("silver-pexer-settings") ?? "{}");
  return existingSettings[key];
}
function setLevelUp(skill, value) {
  const levelUp = getItem("levelUp");
  const newLevelUp = { ...levelUp };
  newLevelUp[skill] = value;
  const sum = Object.values(newLevelUp).reduce((partialSum, s) => partialSum + +s, 0);
  if (sum > 5) {
    throw new Error("sum cannot be over 5");
  }
  setItem("levelUp", newLevelUp);
}
function getLevelUp(skill) {
  const levelUp = getItem("levelUp");
  return levelUp?.[skill];
}
function addMonsterOption(monsterOption) {
  const monsterOptions = getItem("monsterOptions") ?? [];
  const newMonsterOptions = [...monsterOptions.filter((mo) => mo.name !== monsterOption.name), monsterOption];
  setItem("monsterOptions", newMonsterOptions);
}
var SilverPexerSettings = {
  setItem,
  getItem,
  setLevelUp,
  getLevelUp,
  addMonsterOption
};

// src/plugins/autoAttack.ts
async function apply() {
  const monsterToAttack = SilverPexerSettings.getItem("monster");
  const attackWith = SilverPexerSettings.getItem("attack");
  if (!monsterToAttack || !attackWith) {
    return;
  }
  const paElement = document.querySelector(".label-pa");
  const paMatches = paElement.innerText.match(/[0-9]+/);
  if (paMatches) {
    const minPa = SilverPexerSettings.getItem("minPa") ?? config.minPa;
    if (+paMatches[0] <= +minPa) {
      return;
    }
  }
  const monsters = Array.from(document.querySelectorAll(".monster-card") ?? []);
  const monster = monsters.find((m) => m.innerHTML.includes(monsterToAttack));
  if (monster) {
    const rapidAttackButton = monster.querySelector(`img[src$="${attackWith}"]`);
    const initialHtml = monster.innerHTML;
    const monsterKilledOrInjured = () => !document.body.contains(monster) || initialHtml !== monster.innerHTML;
    monster.style.opacity = "0.8";
    await doThenWait(() => rapidAttackButton.click(), () => monsterKilledOrInjured());
    monster.style.opacity = "1";
  }
}
var autoAttack = {
  name: "auto-attack",
  apply,
  loop: true
};

// src/utils/sleep.ts
function sleep(milliseconds) {
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
}

// src/plugins/autoHeal.ts
async function apply2() {
  const lifeGauge = document.querySelector(".carac-gauge > .life");
  if (!lifeGauge) {
    return;
  }
  const lifePercentText = lifeGauge.style.width;
  const groups = lifePercentText.match(/^([0-9]+)(.[0-9]+)?%$/);
  if (!groups) {
    return;
  }
  const lifePercent = +groups[1];
  if (lifePercent < 100) {
    const healIndex = +(SilverPexerSettings.getItem("heal") ?? NaN);
    if (isNaN(healIndex)) {
      return;
    }
    const quickShortcuts = document.querySelectorAll(".character-details .label-name a > img");
    const healShortcut = quickShortcuts[healIndex];
    healShortcut.click();
    await sleep(100);
  }
}
var autoHeal = {
  name: "auto-heal",
  apply: apply2,
  loop: true
};

// src/plugins/autoLevelUp.ts
var skillsMap = {
  ["constitution" /* constitution */]: "constitution" /* constitution */,
  ["force" /* force */]: "puissance",
  ["agilite" /* agilite */]: "agilite" /* agilite */,
  ["intelligence" /* intelligence */]: "intelligence" /* intelligence */
};
function apply3() {
  const levelUp = SilverPexerSettings.getItem("levelUp");
  if (!levelUp) {
    return;
  }
  let count = 0;
  for (const [key, value] of Object.entries(levelUp)) {
    const increaseButton = document.querySelector(`input.btn-success[onclick*="${skillsMap[key]}"]`);
    if (increaseButton) {
      for (let i = 0; i < +value; i++) {
        increaseButton.click();
        count++;
      }
    }
  }
  if (count === 5) {
    setTimeout(() => {
      const submitButton = document.querySelector('input[type="submit"].btn-action');
      submitButton.click();
    }, config.levelUpTimeout);
  }
}
var autoLevelUp = {
  name: "auto-levelup",
  apply: apply3
};

// src/plugins/autoLoot.ts
async function apply4() {
  const itemsOnGround = Array.from(document.querySelectorAll(".element_on_floor > img") ?? []).reverse();
  for (const item of itemsOnGround) {
    const parent = item.parentElement;
    if (!parent) {
      return;
    }
    const initialText = parent.innerText;
    const itemUpdated = () => initialText !== parent.innerText || !document.body.contains(item);
    await doThenWait(() => item.click(), itemUpdated);
  }
}
var autoLoot = {
  name: "auto-loot",
  apply: apply4,
  loop: true
};

// src/plugins/disableNotificationAnimation.ts
function apply5() {
  var style = document.createElement("style");
  style.innerHTML = `.marquee-text-text {
        -webkit-animation: none !important;
        -moz-animation: none !important;
        -o-animation: none !important;
        -ms-animation: none !important;
        animation: none !important;
    }`;
  document.head.appendChild(style);
}
var disableNotificationAnimation = {
  name: "disable-notification-animation",
  apply: apply5
};

// src/plugins/fixCharacter.ts
function apply6() {
  var style = document.createElement("style");
  style.innerHTML = `
    /* remove tabs scrolling */
    .items-container {
        overflow: visible !important;
        height: auto !important;
    }

    .item-description {
        overflow: visible !important;
        max-height: unset !important;
        min-height: 100px !important;
    }

    /* remove challenges scrolling */
    #tab-challenges > div {
        overflow: visible !important;
        height: auto !important;
    }
    `;
  document.head.appendChild(style);
}
var fixChallenges = {
  name: "fix-challenges",
  apply: apply6
};

// src/plugins/fixImagesSize.ts
function apply7() {
  var style = document.createElement("style");
  style.innerHTML = `
    img[src^="/storage/members/"] {
        max-width: 80px;
        max-height: 80px;
    }
    img[src^="/storage/items/"] {
        max-width: 70px;
        max-height: 70px;
    }
    `;
  document.head.appendChild(style);
}
var fixImagesSize = {
  name: "fix-images-size",
  apply: apply7
};

// src/plugins/fixShop.ts
function apply8() {
  var style = document.createElement("style");
  style.innerHTML = `
    #shopElements, #characterElements {
        overflow-y: visible !important;
        height: auto !important;
    }
    `;
  document.head.appendChild(style);
}
var fixShop = {
  name: "fix-shop",
  apply: apply8
};

// src/plugins/privatePlugins.ts
var privatePlugins = {
  map: [autoHeal, autoLoot, autoAttack],
  levelup: [autoLevelUp],
  guild: [],
  castle: [],
  shop: [fixImagesSize, fixShop],
  character: [fixChallenges],
  default: [disableNotificationAnimation]
};

// src/config.ts
var config = {
  minPa: "50",
  levelUpTimeout: 1500,
  loopInterval: 100,
  waitInterval: 10,
  waitTimeout: 2500,
  plugins: privatePlugins
};

// src/silverHtml.ts
var attacks = {
  attaque: "sword3.png",
  berserk: "sword4.png",
  sort1: "magic.png",
  sort2: "magic2.png"
};

// src/options/buildSelect.ts
function buildSelect(options, settingKey) {
  const select = document.createElement("select");
  select.style.maxWidth = "120px";
  const emptyOption = document.createElement("option");
  emptyOption.innerText = "-";
  emptyOption.value = `disable-auto-${settingKey}`;
  select.appendChild(emptyOption);
  const entries = Object.entries(options).sort();
  for (const [key, value] of entries) {
    const option = document.createElement("option");
    option.innerText = key;
    option.value = `${value}`;
    option.selected = value === SilverPexerSettings.getItem(settingKey);
    select.appendChild(option);
  }
  select.addEventListener("change", () => {
    SilverPexerSettings.setItem(settingKey, select.value);
  });
  return select;
}

// src/options/pickMonster.ts
function pickMonster() {
  let picking = true;
  const previousCursor = document.body.style.cursor;
  document.body.style.cursor = "crosshair";
  const monsters = Array.from(document.querySelectorAll(".monster"));
  for (const monster of monsters) {
    const bg = monster.style.backgroundColor;
    monster.addEventListener("mouseover", () => {
      if (!picking)
        return;
      monster.style.backgroundColor = "rgba(255, 255, 255, .3)";
    });
    monster.addEventListener("mouseout", () => {
      if (!picking)
        return;
      monster.style.backgroundColor = bg;
    });
    monster.addEventListener("click", (e) => {
      if (!picking)
        return;
      monster.style.backgroundColor = bg;
      const name = monster.innerText;
      const image = monster.querySelector("a > .img_container > img")?.getAttribute("src");
      if (!image) {
        alert(`Error monster without image: ${name}`);
      } else {
        SilverPexerSettings.addMonsterOption({ name, image });
        refreshAutoAttack();
      }
      picking = false;
      document.body.style.cursor = previousCursor;
      e.preventDefault();
    });
  }
}

// src/options/autoAttack.ts
function buildAutoAttackTable() {
  const autoAttackTable = document.createElement("table");
  autoAttackTable.innerHTML = `
    <tr>
        <td><label for="swp-monster-select">Monstre XP</label>&nbsp;</td>
        <td id="swp-monster"></td>
        <td>&nbsp;<a href="javascript:void(0)" id="monsters-pick">ajouter</a></td>
    </tr>
    <tr>
        <td><label for="swp-attack-select">Attaque</label>&nbsp;</td>
        <td id="swp-attack"></td>
        <td></td>
    </tr>
    <tr>
        <td><label for="swp-heal-select">Heal</label>&nbsp;</td>
        <td id="swp-heal"></td>
        <td></td>
    </tr>
    <tr>
        <td><label for="swp-minpa-input">Min PA</label>&nbsp;</td>
        <td><input id="swp-minpa-input" type="number" style="max-width: 10ch" /></td>
        <td></td>
    </tr>
    `;
  const monsterOptions = (SilverPexerSettings.getItem("monsterOptions") ?? []).reduce((accumulator, monster) => {
    return { ...accumulator, [monster.name]: monster.image };
  }, {});
  const monsterSelect = buildSelect(monsterOptions, "monster");
  monsterSelect.id = "swp-monster-select";
  autoAttackTable.querySelector("#swp-monster")?.appendChild(monsterSelect);
  const attackSelect = buildSelect(attacks, "attack");
  attackSelect.id = "swp-attack-select";
  autoAttackTable.querySelector("#swp-attack")?.appendChild(attackSelect);
  const healSelect = buildSelect(Shortcuts, "heal");
  healSelect.id = "swp-heal-select";
  autoAttackTable.querySelector("#swp-heal")?.appendChild(healSelect);
  const monsterPick = autoAttackTable.querySelector("#monsters-pick");
  monsterPick.addEventListener("click", pickMonster);
  const minPaInput = autoAttackTable.querySelector("#swp-minpa-input");
  if (minPaInput) {
    minPaInput.value = SilverPexerSettings.getItem("minPa") ?? config.minPa;
    minPaInput?.addEventListener("change", () => {
      SilverPexerSettings.setItem("minPa", minPaInput.value);
    });
  }
  return autoAttackTable;
}
function refreshAutoAttack() {
  const autoAttackTable = buildAutoAttackTable();
  document.querySelector("#swp-autoattack")?.replaceChildren(autoAttackTable);
}

// src/options/buildLevelUp.ts
function buildLevelUpTable() {
  const skills = Object.values(Skills);
  const table = document.createElement("table");
  for (const skill of skills) {
    const line = document.createElement("tr");
    line.innerHTML = `
        <td><label for="swp-level-up-${skill}-input">${skill}&nbsp;</label></td>
        <td id="swp-level-up-${skill}"></td>
        `;
    const input = document.createElement("input");
    input.id = `swp-level-up-${skill}-input`;
    input.type = "number";
    input.min = "0";
    input.max = "5";
    input.style.maxWidth = "10ch";
    input.value = SilverPexerSettings.getLevelUp(skill) ?? "0";
    input.addEventListener("change", () => {
      try {
        SilverPexerSettings.setLevelUp(skill, input.value);
      } catch (e) {
        input.value = (+input.value - 1).toString();
      }
    });
    line.querySelector(`td#swp-level-up-${skill}`)?.appendChild(input);
    table.appendChild(line);
  }
  return table;
}

// src/options/buildSettings.ts
function buildSettings() {
  const settingsDiv = document.createElement("div");
  settingsDiv.className = "card";
  settingsDiv.style.position = "absolute";
  settingsDiv.style.bottom = "57px";
  settingsDiv.style.right = "10px";
  settingsDiv.style.display = "none";
  settingsDiv.style.width = "270px";
  const settingsBody = document.createElement("div");
  settingsBody.className = "card-body";
  settingsBody.innerHTML = `
    <h5>SilverPexer Settings</h5>
    
    <h6 style="margin-top: 1em;">Auto Attack</h6>
    <div id="swp-autoattack"></div>
    
    <h6 style="margin-top: 1em;">Level Up</h6>
    <div id="swp-levelup"></div>

    <h6 style="margin-top: 1em;">Autres</h6>
    <ul>
        <li><a href="javascript:void(0)" id="monsters-empty">Reset liste monstres</a></li>
    </ul>
    `;
  const autoAttackTable = buildAutoAttackTable();
  settingsBody.querySelector("#swp-autoattack")?.appendChild(autoAttackTable);
  const levelUpTable = buildLevelUpTable();
  settingsBody.querySelector("#swp-levelup")?.appendChild(levelUpTable);
  settingsBody.querySelector("#monsters-empty")?.addEventListener("click", () => {
    const ok = confirm("Voulez-vous enlever tous les monstres ajout\xE9s ?");
    if (ok) {
      SilverPexerSettings.setItem("monsterOptions", void 0);
      SilverPexerSettings.setItem("monster", void 0);
      refreshAutoAttack();
    }
  });
  settingsDiv.appendChild(settingsBody);
  return settingsDiv;
}

// src/options/addOptions.ts
function addOptions() {
  const messengerFixed = document.querySelector("#MessengerFixedController");
  if (!messengerFixed) {
    return;
  }
  const settingsDiv = buildSettings();
  const settingsImage = document.createElement("img");
  settingsImage.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/Icons8_flat_settings.svg/32px-Icons8_flat_settings.svg.png";
  settingsImage.className = "img-fluid";
  settingsImage.style.width = "24px";
  settingsImage.style.height = "24px";
  settingsImage.alt = "SilverPexer Settings";
  settingsImage.addEventListener("click", () => {
    if (settingsDiv.style.display === "none") {
      settingsDiv.style.display = "block";
    } else {
      settingsDiv.style.display = "none";
    }
  });
  const optionsDiv = document.createElement("div");
  optionsDiv.appendChild(settingsImage);
  const optionsListItem = document.createElement("li");
  optionsListItem.appendChild(optionsDiv);
  messengerFixed?.appendChild(settingsDiv);
  messengerFixed?.querySelector("ul")?.appendChild(optionsListItem);
}

// src/utils/loop.ts
async function loop(action, interval) {
  while (true) {
    try {
      await action();
    } catch (e) {
      console.error("loop error:", e);
    }
    await sleep(interval);
  }
}

// src/index.ts
(async function() {
  "use strict";
  addOptions();
  const groups = window.location.pathname.match(/^\/([^\/]+)(?:\/)?([^\/]+)?/);
  if (!groups) {
    console.warn("Could not parse pathname: ", window.location.pathname);
    return;
  }
  const [_path, page, _subPage] = groups;
  const plugins = [...config.plugins.default ?? [], ...config.plugins[page] ?? []];
  const oncePlugins = plugins.filter((p) => !p.loop);
  for (const plugin of oncePlugins) {
    await plugin.apply();
  }
  const loopPlugins = plugins.filter((p) => p.loop);
  await loop(async () => {
    for (const plugin of loopPlugins) {
      await plugin.apply();
    }
  }, config.loopInterval);
})().catch(console.error);