Sploop.io [Visual Extension]

New visuals elements [Tracers, HitBoxes, HP indicator, Setting menu]

// ==UserScript==
// @name           Sploop.io [Visual Extension]
// @name:ru        Sploop.io [Визуальное расширение]
// @description    New visuals elements [Tracers, HitBoxes, HP indicator, Setting menu]
// @description:ru Новые визуальные элементы [Tracers, HitBoxes, HP indicator, Setting menu]
// @namespace      https://greasyfork.org/ru/users/759782-nudo
// @version        2.1
// @author         Nudo#3310
// @match          *://sploop.io/*
// @require        http://code.jquery.com/jquery-3.3.1.min.js
// @require        https://code.jquery.com/ui/1.12.0/jquery-ui.min.js
// @grant          none
// ==/UserScript==

class Visuals {
    constructor() {
        this.text = {
            color: {
                all: "#fff",
                rainbow: false
            },
            visible: 1
        }
        this.tracers = {
            active: true,
            disttag: true,
            dashline: false,
            color: {
                entity: "#cc5151",
                ally: "#a4cc4f",
                rainbow: false
            },
            size: 1,
            visible: 1
        }
        this.hitboxes = {
            active: false,
            dashline: false,
            color: {
                all: "#5174cd",
                rainbow: false
            },
            size: 1,
            visible: 1
        }
        this.rainbow = {
            old: Date.now(),
            hue: 0,
            power: 3,
            time: 10
        }
        this.offset = [0, Date.now()]
    }
    rainbowColor() {
        if (!this.rainbow.old || Date.now() - this.rainbow.old >= this.rainbow.time) {
            this.rainbow.hue += this.rainbow.power * Math.random()
            this.rainbow.old = Date.now()
        }
        visuals.rb = `hsl(${this.rainbow.hue}, 100%, 50%)`
    }
    drawText(text, x, y) {
        Context.save()
        Context.font = '18px "Baloo Paaji"'
        Context.lineWidth = 8
        Context.strokeStyle = "#3d3f42"
        Context.globalAlpha = this.text.visible
        Context.textAlign = 'center'
        Context.fillStyle = this.text.color.rainbow ? visuals.rb : this.text.color.all
        Context.strokeText(text, x, y)
        Context.fillText(text, x, y)
        Context.restore()
    }
    updateOffset() {
        if (!this.offset[1] || Date.now() - this.offset[1] >= 10) {
            this.offset[0]++
            this.offset[1] = Date.now()
        }
    }
    dashLine() {
        Context.setLineDash([18, 6, 6, 6])
        Context.lineDashOffset = -visuals.offset[0]
    }
}

let visuals = new Visuals()

class Tracers {
    constructor() {
        this.allAlly = []
        this.allEntity = []
        this.localPlayer = {
            active: false,
            x: 0,
            y: 0
        }
    }
    drawDistTag(x, y, dist) {
        if (!visuals.tracers.disttag) return
        Context.save()
        Context.font = '18px "Baloo Paaji"'
        Context.lineWidth = 8
        Context.strokeStyle = "#3d3f42"
        Context.globalAlpha = visuals.text.visible
        Context.fillStyle = visuals.text.color.rainbow ? visuals.rb : visuals.text.color.all
        Context.strokeText(dist, x, y)
        Context.fillText(dist, x, y)
        Context.restore()
    }
    draw(x, y, x2, y2, color) {
        if (!visuals.tracers.active) return
        Context.save()
        Context.lineCap = "round"
        Context.lineWidth = visuals.tracers.size
        Context.globalAlpha = visuals.tracers.visible
        Context.beginPath()
        if (visuals.tracers.dashline) visuals.dashLine()
        Context.strokeStyle = color
        Context.moveTo(x, y)
        Context.lineTo(x2, y2)
        Context.stroke()
        Context.restore()
    }
}

let tracers = new Tracers()

class HitBoxes {
    static draw(x, y, width, height) {
        if (!visuals.hitboxes.active) return
        Context.save()
        Context.lineWidth = visuals.hitboxes.size
        Context.globalAlpha = visuals.hitboxes.visible
        if (visuals.hitboxes.dashline) visuals.dashLine()
        Context.strokeStyle = visuals.hitboxes.color.rainbow ? visuals.rb : visuals.hitboxes.color.all
        Context.strokeRect(x, y, width, height)
        Context.restore()
    }
}

let Context;

let { clearRect, fillRect, fillText, drawImage } = CanvasRenderingContext2D.prototype

CanvasRenderingContext2D.prototype.clearRect = function(x, y, width, height) {
    if (this.canvas.id === "game-canvas") {
        Context = this.canvas.getContext("2d")
        visuals.rainbowColor()
        tracers.allEntity = []
        tracers.allAlly = []
    }
    return clearRect.apply(this, arguments);
}

CanvasRenderingContext2D.prototype.drawImage = function(image, x, y, width, height, dx, dy, dwidth, dheight) {
    if (tracers.localPlayer.active && typeof image.src == 'string') {
        let ff = image.src.split("/")
        if (ff[4] == "skins"
            || ff[4] == "entity"
            && !ff[5].includes("inv_")
            && !ff[5].includes("map")
            && !ff[5].includes("resource")
            && !ff[5].includes("health")
            && !ff[5].includes("button")
            && !ff[5].includes("skull")) {
            HitBoxes.draw(x, y, width, height)
        }
    }
    return drawImage.apply(this, arguments);
}

CanvasRenderingContext2D.prototype.fillRect = function(x, y, width, height) {
    if (document.getElementById("homepage").style.display == "none") {
        visuals.updateOffset()
        if (this.fillStyle == "#a4cc4f") {
            tracers.allAlly.push({
                x: x + 45,
                y: y - 70
            })
            tracers.localPlayer.active = true
            tracers.localPlayer.x = tracers.allAlly[0].x
            tracers.localPlayer.y = tracers.allAlly[0].y
            visuals.drawText(`HP: ${~~(width / 95 * 100)}%`, x + 45, y + 40)
            visuals.drawText(`VisualExtension`, tracers.localPlayer.x, tracers.localPlayer.y + 125)
            if (tracers.allAlly[1]) {
                tracers.allAlly.forEach(ally => {
                    if (ally.x != tracers.localPlayer.x) {
                        let color = (visuals.tracers.color.rainbow ? visuals.rb : visuals.tracers.color.ally)
                        tracers.draw(tracers.localPlayer.x, tracers.localPlayer.y, ally.x, ally.y, color)
                        tracers.drawDistTag((tracers.localPlayer.x + ally.x) / 2, (tracers.localPlayer.y + ally.y) / 2, ~~(Math.hypot(tracers.localPlayer.y - ally.y, tracers.localPlayer.x - ally.x)))
                    }
                })
            }
        }
        if (this.fillStyle == "#cc5151" && tracers.localPlayer.active) {
            visuals.drawText(`HP: ${~~(width / 95 * 100)}%`, x + 45, y + 40)
            tracers.allEntity.push({
                x: x + 45,
                y: y - 70
            })
            tracers.allEntity.forEach(enemy => {
                let color = (visuals.tracers.color.rainbow ? visuals.rb : visuals.tracers.color.entity)
                tracers.draw(tracers.localPlayer.x, tracers.localPlayer.y, enemy.x, enemy.y, color)
                tracers.drawDistTag((tracers.localPlayer.x + enemy.x) / 2, (tracers.localPlayer.y + enemy.y) / 2, ~~(Math.hypot(tracers.localPlayer.y - enemy.y, tracers.localPlayer.x - enemy.x)))
            })
        }
    } else {
        tracers.localPlayer.active = false
    }
    return fillRect.apply(this, arguments)
}

let Menu = `
<div class="menu-holder flex-c">
  <div class="menu-wrapper">
    <div class="menu-container">
      <div class="menu-title flex-c text-shadowed-4">
        <span>VisualExtension</span>
      </div>
      <div class="menu-content scrollbar text-shadowed-3">
        <ul class="tracers-content">
          <li class="subcontent-subtitle" style="font-size: 18px;">Tracer</li>
          <li class="setting-box">
            <div class="name-box">Active</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="tracer-active" checked><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box">
            <div class="name-box">RainbowColor</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="tracer-rainbow"><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box" title="When there are a lot of tracers, it's a lower your FPS!">
            <div class="name-box">DashLine</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="tracer-dashline"><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box">
            <div class="name-box">DistTag</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="tracer-disttag" checked><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box">
            <div class="name-box">AllyColor</div>
            <div class="action-box"><input type="color" id="tracers-ally-color" value="#a4cc4f"></div>
          </li>
          <li class="setting-box">
            <div class="name-box">EntityColor</div>
            <div class="action-box"><input type="color" id="tracers-entity-color" value="#cc5151"></div>
          </li>
          <li class="setting-box">
            <div class="name-box">Size</div>
            <div class="action-box">
              <div class="range-wrapper"><input type="range" class="pointer" id="tracers-size" value="1" min="1" step="any" max="10"><div class="range-value flex-c text-shadowed-3" id="tracers-size-value">1</div></div>
            </div>
          </li>
          <li class="setting-box">
            <div class="name-box">Visible</div>
            <div class="action-box">
              <div class="range-wrapper"><input type="range" class="pointer" id="tracers-visible" value="1" min="0" step="any" max="1"><div class="range-value flex-c text-shadowed-3" id="tracers-visible-value">1</div></div>
            </div>
          </li>
        </ul>
        <ul class="text-content">
          <li class="subcontent-subtitle" style="font-size: 18px;">Text</li>
          <li class="setting-box">
            <div class="name-box">RainbowColor</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="text-rainbow"><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box">
            <div class="name-box">Color</div>
            <div class="action-box"><input type="color" id="text-all-color" value="#ffffff"></div>
          </li>
          <li class="setting-box">
            <div class="name-box">Visible</div>
            <div class="action-box">
              <div class="range-wrapper"><input type="range" class="pointer" id="text-visible" value="1" min="0" step="any" max="1"><div class="range-value flex-c text-shadowed-3" id="text-visible-value">1</div></div>
            </div>
          </li>
        </ul>
        <ul class="hitboxes-content">
          <li class="subcontent-subtitle" style="font-size: 18px;">HitBox</li>
          <li class="setting-box">
            <div class="name-box">Active</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="hitbox-active"><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box">
            <div class="name-box">RainbowColor</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="hitbox-rainbow"><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box" title="When there are a lot of hitboxes, it's a lower your FPS!">
            <div class="name-box">DashLine</div>
            <div class="action-box"><div class="control-group"><label class="control control-checkbox" style="display:unset;padding-top:4px;"><input type="checkbox" id="hitbox-dashline"><div class="control_indicator"></div></label></div></div>
          </li>
          <li class="setting-box">
            <div class="name-box">Color</div>
            <div class="action-box"><input type="color" id="hitbox-all-color" value="#5174cd"></div>
          </li>
          <li class="setting-box">
            <div class="name-box">Size</div>
            <div class="action-box">
              <div class="range-wrapper"><input type="range" class="pointer" id="hitbox-size" value="1" min="1" step="any" max="10"><div class="range-value flex-c text-shadowed-3" id="hitbox-size-value">1</div></div>
            </div>
          </li>
          <li class="setting-box">
            <div class="name-box">Visible</div>
            <div class="action-box">
              <div class="range-wrapper"><input type="range" class="pointer" id="hitbox-visible" value="1" min="0" step="any" max="1"><div class="range-value flex-c text-shadowed-3" id="hitbox-visible-value">1</div></div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</div>
<style>
.range-value {
  width: 26px;
  margin-left: 5px;
  margin-right: -5px;
}
.range-wrapper {
  display: flex;
  margin-right: 5px;
  align-items: center;
}
.name-box {
  dont-size: 16px;
}
.setting-box {
  margin-bottom: 8px;
  display: flex;
  justify-content: space-between;
  width: 100%;
  padding-left: 3px;
  padding-right: 3px;
}
.tracers-content, .text-content, .hitboxes-content {
  margin-left: 5px;
}
.menu-content {
  display: flex;
  flex-direction: column;
  width: 214px;
  height: 265px;
  background: rgb(20 20 20 / 30%);
  border-radius: 10px;
  overflow-y: scroll;
  overflow-x: hidden;
  border: 3px solid #141414;
  box-shadow: inset 0 5px 0 rgb(20 20 20 / 40%);
  margin-left: 3px;
}
.menu-title span {
  font-size: 20px;
  color: #f0ece0;
}
.menu-title {
  width: 100%;
  height: 40px;
}
.menu-container {
  position: relative;
  left: -215px;
  display: flex;
  flex-direction: column;
}
.menu-wrapper {
  width: 40px;
  height: 325px;
  background: rgba(40, 45, 34, 0.6);
  border-radius: 0 15px 15px 0;
  border: 5px solid #141414;
  border-left: none;
  box-shadow: none;
  opacity: .5;
  transition: .5s all;
}
.menu-wrapper:hover {
  box-shadow: inset 0 4px 0 #4e5645, inset 0 -4px 0 #384825, 0px 2px 0 5px rgb(20 20 20 / 30%), 0px 0px 0 15px rgb(20 20 20 / 10%);
}
.menu-holder {
  position: absolute;
  top: 0;
  height: 100%;
}
.flex-c {
  display: flex;
  justify-content: center;
  align-items: center;
}
.flex-sb-c {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
input[type="color"] {
  -webkit-appearance: none;
  border: none;
  background: none;
  width: 20px;
  height: 20px;
  border: 4px solid #141414;
  border-radius: 50%;
}
input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
}
input[type="color"]::-webkit-color-swatch {
  border: none;
  border-radius: 50%;
}
input[type=color]::-moz-focus-inner {
  border: none;
  padding: 0;
  border-radius: 50%;
}
input[type=color]::-moz-color-swatch {
  border: none;
  border-radius: 50%;
}
input[type="range"] {
  -webkit-appearance: none;
  width: 80px;
  height: 12px;
  background: rgba(40, 45, 34, 0.6);;
  border: 3px solid #141414;
  outline: none;
}
input[type="range"]::-webkit-slider-thumb {
  cursor: url(img/ui/cursor-pointer.png) 6 0, pointer;
  -webkit-appearance: none;
  width: 20px;
  border-radius: 4px;
  height: 20px;
  background: #f0ece0;
  border: 4px solid #141414;
  position: relative;
  z-index: 3;
}
</style>
`

$("body").append(Menu)

$(".menu-wrapper").mouseenter(() => {
    $(".menu-container").animate({left: "0px"}, 750)
    $(".menu-wrapper").animate({ width: "225px", opacity: "1" }, 250)
}).mouseleave(() => {
    $(".menu-container").animate({left: "-215px"}, 750)
    $(".menu-wrapper").animate({ width: "40px", opacity: ".5" }, 250)
})

$("#tracers-visible").on("input", () => {
    visuals.tracers.visible = $("#tracers-visible").val()
    let fn = String((parseInt(visuals.tracers.visible * 100)) / 100)
    $("#tracers-visible-value").text(fn.includes("0.0") ? "." + fn.split("0.")[1] : fn.includes("0.") ? fn.split("0")[1] : fn)
})

$("#text-visible").on("input", () => {
    visuals.text.visible = $("#text-visible").val()
    let fn = String((parseInt(visuals.text.visible * 100)) / 100)
    $("#text-visible-value").text(fn.includes("0.0") ? "." + fn.split("0.")[1] : fn.includes("0.") ? fn.split("0")[1] : fn)
})

$("#hitbox-visible").on("input", () => {
    visuals.hitboxes.visible = $("#hitbox-visible").val()
    let fn = String((parseInt(visuals.hitboxes.visible * 100)) / 100)
    $("#hitbox-visible-value").text(fn.includes("0.0") ? "." + fn.split("0.")[1] : fn.includes("0.") ? fn.split("0")[1] : fn)
})

$("#hitbox-size").on("input", () => {
    visuals.hitboxes.size = $("#hitbox-size").val()
    let fn = String((parseInt(visuals.hitboxes.size * 100)) / 100)
    $("#hitbox-size-value").text(fn.includes("0.0") ? "." + fn.split("0.")[1] : fn.includes("0.") ? fn.split("0")[1] : fn)
})

$("#tracers-size").on("input", () => {
    visuals.tracers.size = $("#tracers-size").val()
    let fn = String((parseInt(visuals.tracers.size * 100)) / 100)
    $("#tracers-size-value").text(fn.includes("0.0") ? "." + fn.split("0.")[1] : fn.includes("0.") ? fn.split("0")[1] : fn)
})

$("#tracers-ally-color").on("input", () => visuals.tracers.color.ally = $("#tracers-ally-color").val())

$("#tracers-entity-color").on("input", () => visuals.tracers.color.entity = $("#tracers-entity-color").val())

$("#tracer-rainbow").on("input", () => visuals.tracers.color.rainbow = !visuals.tracers.color.rainbow)

$("#tracer-disttag").on("input", () => visuals.tracers.disttag = !visuals.tracers.disttag, )

$("#tracer-dashline").on("input", () => visuals.tracers.dashline = !visuals.tracers.dashline)

$("#text-all-color").on("input", () => visuals.text.color.all = $("#text-all-color").val())

$("#text-rainbow").on("input", () => visuals.text.color.rainbow = !visuals.text.color.rainbow)

$("#hitbox-all-color").on("input", () => visuals.hitboxes.color.all = $("#hitbox-all-color").val())

$("#hitbox-rainbow").on("input", () => visuals.hitboxes.color.rainbow = !visuals.hitboxes.color.rainbow)

$("#hitbox-dashline").on("input", () => visuals.hitboxes.dashline = !visuals.hitboxes.dashline)

$("#hitbox-active").on("input", () => visuals.hitboxes.active = !visuals.hitboxes.active)

$("#tracer-active").on("input", () => visuals.tracers.active = !visuals.tracers.active)