// ==UserScript==
// @name Baka extensions tool
// @version 1.41
// @namespace baka-extensions-tool
// @updateURL https://raw.githubusercontent.com/xzfc/baka-extensions-tool/master/baka_extentsions_tool.user.js
// @require http://xregexp.com/v/3.0.0/xregexp-min.js
// @include http://agar.io/*
// @match http://ogar.mivabe.nl/?ip=*
// @grant none
// @run-at document-start
// ==/UserScript==
(function バカスクリプト() {
var version = "1.41"
setConf({wsUri: "ws://146.185.131.246:8000/",
quickTemplates: {
Backquote: {
_049: ['К', 'Покорми!'],
S049: ['!К', 'Не корми!'],
_050: ['ПК', 'Пульни колючку!'],
S050: ['ЧК', 'Чисти колючки!'],
_051: ['Х', 'Скушай хвостик!'],
S051: ['!Х', 'Не кушай хвостик!'],
_052: ['✖', 'Отходим!'],
S052: ['⧁', 'Наступаем!'],
_053: ['Д', 'Делись!'],
S053: ['!Д', 'Не делись!'],
},
_097: ['↙', 'Левый нижний угол'],
_098: ['↓', 'Центр внизу'],
_099: ['↘', 'Правый нижний угол'],
_100: ['←', 'Центр слева'],
_101: ['⨀', 'Центр'],
_102: ['→', 'Центр справа'],
_103: ['↖', 'Левый верхний угол'],
_104: ['↑', 'Центр сверху'],
_105: ['↗', 'Правый верхний угол'],
},
keys: {
Shift_Comma: "move_chat",
Shift_Period: "move_map",
Shift_Tab: "focus_chat",
Backslash: "toggle_pellets",
Comma: "toggle_chat",
Enter: "focus_chat",
KeyC: ["toggle_eating_mass_guide",
"toggle_active_cell"],
Shift_KeyC: "toggle_split_guide",
KeyX: "toggle_eating_distance_guide",
KeyM: "toggle_mouse_lines",
Shift_KeyM: "toggle_stop",
Period: "toggle_map",
Slash: "toggle_canvas",
Tab: "activate_cell",
Digit1: "activate_cell 0",
Digit2: "activate_cell 1",
Digit3: "activate_cell 2",
Digit4: "activate_cell 3",
Digit5: "activate_cell 4",
Digit6: "activate_cell 5",
Digit7: "activate_cell 6",
Digit8: "activate_cell 7",
Digit9: "activate_cell 15",
F11: "toggle_fullscreen",
KeyE: "double_split",
Shift_KeyE: "quadruple_split",
},
teams:{
baka:{aura: "#00f",
names: [/⑨/]},
zt: {aura: "#a5a",
names: [/ƵŦ/]},
reddit: {aura: "#aa5",
names: [/《ℝ》/]},
brazil: {names: /ᴳᴀᴮ|ŦḰΔ|βҜǤ|ǤΔβ/},
turkey: {names: [/ek[sşŞ\$][iİ]/i,
/[iİ][╦T][µÜ]/i,
/[ʍm][εe][†t][υuü]|ʍε†/i,
/[Ծo][dԺ][tԵ][uü]/i,
/ΆǾĢ|ⒶⓄⒼ/, /ŦĪŦ/,
/\bDH\b/, /[つマづĐÐ][みℋんĦн]|ⒹⒽ|매|Ďℍ|⊃Ⓗ|đħ/]},
other: {names: [/\[(\$|402λ|WAR|AOG|DH|FBI|TUC|EU|TW|AGU|R[iİ]PO|T[iİ]T|EXER)\]/i,
/ヴ[いぃ]p/,
/ⒷⓀ|ⓂⓋⓅ|RZCW|MZDK|Mezdeke/,
/\b(AOG|MKB|MZK|FKS|TİT|HKG)\b/i,
/\b(pkb|ркб|ркв)\b/i, /P₭฿|₱₭฿/,
/[1①❶][8⑧❽]-?[2②❷][5⑤❺]/,
/[ÌÍ]\.M\.P/, "[UP] #1",
/㉹/, /ՁЧ➹7|ջЧ➹Դ/,
/〖ƝƁƘ〗|⟦ƝƁƘ⟧/,
/ҚÕŽ/,
/H&&[₭ᛕK]/,
]},
},
soundList: ["http://146.185.131.246/static/tutturu/0.mp3",
"http://146.185.131.246/static/tutturu/1.mp3",
"http://146.185.131.246/static/tutturu/2.mp3",
"http://146.185.131.246/static/tutturu/3.mp3",
"http://146.185.131.246/static/tutturu/4.mp3",
"http://146.185.131.246/static/tutturu/5.mp3",
"http://146.185.131.246/static/tutturu/6.mp3",
"http://146.185.131.246/static/tutturu/7.mp3"],
sound: {chat:1, quick:1},
showOnlyBakaAura: false,
myAura: "#fff",
bakaAura: "#000",
defaultTeamAura: "#A55",
timeFormat: 0,
mouseControls: true,
fogOfWar: true,
hideJoinLeaveMessages: false,
mapProjection: [-7060, 7060],
mapSize: 256,
mySkinUri: "http://146.185.131.246/static/skins/Jerky_McJerkface.png",
mySkinBig: false,
bakaSkinUri: "http://146.185.131.246/static/skins/cirno.svg",
bakaSkinBig: false,
pelletColor: null,
virusColor: "rgba(128,128,128,0.6)",
eatingMassGuide: {
smallColors: [null, "#00aa00", "#cc66ff"],
bigColors: [null, "#ffbf3d", "#ff3c3c"],
width: 0.6,
},
bgImage: "http://i.imgur.com/E4u6yMZ.jpg",
cellOpacity: 0.8,
viewportBox: {color: "#7f7f7f", width: 10},
worldBox: {color: "#f44336", width: 90},
replaceCursor: true,
stealAnime: false,
})
var userConf
var myName = null
var hasConnected = false
var nextMessageId = 0
var drawPellets = true
var zc = Boolean(g("ZCOverlay"))
var defaultName = "Безымянная сырно"
function g(id) { return document.getElementById(id) }
function isArray(obj, type) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
function includes(array, value) {
return array.indexOf(value) !== -1
}
function setConf(defaults) {
function setDefault(record, field, value) {
if (record[field] === undefined && value !== undefined)
record[field] = value
}
setDefault(window, 'bakaconf', {})
userConf = JSON.parse(JSON.stringify(window.bakaconf))
for (var i in defaults)
if (i !== 'teams')
setDefault(window.bakaconf, i, defaults[i])
setDefault(window.bakaconf, 'teams', {})
for (var i in defaults.teams)
setDefault(window.bakaconf.teams, i, defaults.teams[i])
}
function join(l) {
if (!(l instanceof Array))
l = Array.from(l)
if (l.length === 0)
return ["никого"]
if (l.length <= 1)
return l
var result = []
for (var i = 0; i < l.length; i++) {
result.push(l[i])
if (i < l.length-2)
result.push(", ")
else if (i === l.length-2)
result.push(" и ")
}
return result
}
function formatTime(t) {
function pad(number) { return (number < 10) ? '0' + number : number }
t = new Date(t*1000 + 1000*60*60*3)
var h = pad(t.getUTCHours())
var m = pad(t.getUTCMinutes())
var s = pad(t.getUTCSeconds())
switch(window.bakaconf.timeFormat) {
case 0: return `${h}:${m}:${s} `
case 1: return `${h}:${m} `
case 2: return ``
}
}
var storage = {
get(key) {
if (typeof GM_getValue === 'function')
return GM_getValue(key, null)
else
return localStorage.getItem('baka_' + key)
},
set(key, value) {
if (typeof GM_setValue === 'function')
GM_setValue(key, value)
else
localStorage.setItem('baka_' + key, value)
}
}
var notificator = {
cached: [],
list: "",
unreadCount: 0,
oldTitle: null,
init() {
var el = document.createElement('div')
el.id = "notification"
document.body.appendChild(el)
el.onclick = () => chat.toggle(true)
this.cache()
window.addEventListener("focus",
() => { chat.hidden || this.clear() })
},
cache() {
var list = window.bakaconf.soundList.join("\n")
if (list === this.list)
return
this.list = list
console.log("Update!")
this.cached = window.bakaconf.soundList.map(x => new Audio(x))
},
clear() {
this.unreadCount = 0
g("notification").style.visibility = 'hidden'
if (this.oldTitle !== null) {
document.title = this.oldTitle
this.oldTitle = null
}
},
notify(what) {
if (document.hidden || chat.hidden) {
this.unreadCount++
if (this.oldTitle === null)
this.oldTitle = document.title
document.title = `[${this.unreadCount}] ${this.oldTitle}`
}
if (chat.hidden) {
g("notification").style.visibility = ''
g("notification").textContent = this.unreadCount
}
if (window.bakaconf.sound[what]) {
this.cache()
if (this.cached.length === 0)
return
this.cached[Math.floor(Math.random()*this.cached.length)].play()
}
},
}
var chat = {
hidden: false,
active: false,
usersCount: 0,
init() {
var cbox = document.createElement('div')
cbox.id = 'cbox'
cbox.innerHTML = `
`
document.body.appendChild(cbox)
g("chat_users").onclick = () => send({t:'names'})
g('form').onsubmit = submit
g('carea').onfocus = () => {
this.active = true
cbox.style.opacity = '1'
}
g('carea').onblur = () => {
this.active = false
cbox.style.opacity = '0.7'
}
g('carea').onkeydown = (e) => {
switch (e.keyCode) {
case 38: return submitHistory.up(), false
case 40: return submitHistory.down(), false
}
}
},
move() {
toggleAttribute(g('cbox'), 'data-alt-position')
toggleAttribute(g('baka-labels'), 'data-alt-position')
},
toggle(show) {
this.hidden = show === undefined ? !this.hidden : !show
g('cbox').style.visibility = (this.hidden ? 'hidden' : '')
notificator.clear()
},
focus() {
this.toggle(true)
document.getElementById('carea').focus()
},
blur() { g('carea').blur() },
clickName(e) {
e = e || window.event; e = e.target || e.srcElement
var ca = document.getElementById('carea')
ca.value = `${e.textContent}: ${ca.value}`
this.focus()
},
setUsersCount(add, value) {
if (add)
this.usersCount += value
else
this.usersCount = value
g("chat_users").textContent =
this.usersCount >= 0 ? this.usersCount : "#"
},
preserveScroll(fun) {
var msgbox = document.getElementById('msgsbox')
var scroll = 0 ===
msgbox.scrollTop + msgbox.offsetHeight - msgbox.scrollHeight
fun()
if (scroll)
msgbox.lastChild.scrollIntoView()
},
}
function connectChat() {
function reconnectButton(e) {
e = e || window.event;e = e.target || e.srcElement
e.parentNode.removeChild(e)
connectChat()
}
ignore.reset()
var reconnect = false, closed = false
var ws = new WebSocket(window.bakaconf.wsUri)
var myId = null
var welcomed = false
ws.binaryType = "arraybuffer"
ws.onopen = (evt) => {
mapSender.reset()
send({t: "version", version: version,
v72: (window.v72===undefined?0:1),
expose: (window.agar===undefined?0:1) })
var auth_token = storage.get('auth_token')
if (auth_token !== null)
send({t:"auth", token:auth_token})
if (myName !== null)
send({t: "name", "name": myName})
send({t:"messages", startingFromId: nextMessageId})
send({t:"names"})
}
ws.onclose = (evt) => {
if (closed) return
chat.setUsersCount(false, -1)
if (reconnect) {
closed = true
addLine({message:['Переподключаюсь~']})
return connectChat()
}
if (hasConnected) {
hasConnected = false
return window.setTimeout(connectChat, 1000)
} else {
addLine({message:[
"Вебсокет закрыт. ",
aButton("переподключиться к вебсокету", reconnectButton)]})
}
notificator.notfy('system')
}
ws.onerror = (evt) => {
if (closed) return
console.log(evt)
addLine({message:"Ошибка вебсокета"})
}
ws.reconnect = () => {
reconnect = true
ws.close()
window.setTimeout(() => {
if (closed) return
closed = true
addLine({message:['Переподключаюсь~~']})
connectChat()
}, 1000)
}
ws.onmessage = (evt) => {
hasConnected = true
if (closed) return
if (evt.data instanceof window.ArrayBuffer)
onmessage_binary(new DataView(evt.data))
else
onmessage_json(JSON.parse(evt.data))
}
function onmessage_json(d) {
var sender = {i:d.i, name:d.f, premium:d.premium, mode:"message"}
function notify(what) {
if (myId !== d.i && welcomed)
notificator.notify(what)
}
if (d.I !== undefined)
nextMessageId = d.I + 1
switch(d.t) {
case "names":
var namesList = d.names
.filter(n => n.name !== "")
.map(aName)
var nonameCount = d.names.length - namesList.length
if (nonameCount === 0) {/* do nothing */}
else if (nonameCount === 1)
namesList.push("одна безымянная сырно" +
(myName === ""?" (это ты)":""))
else
namesList.push(nonameCount + " безымянных сырно" +
(myName === ""?" (включая тебя)":""))
var chatmsg = ["В чате ", ...join(namesList), "."];
addLine({time:d.T,
message:chatmsg})
chat.setUsersCount(false, d.names.length)
break
case "message":
var tokens = d.text.trim().split(/ +/)
if (tokens[0] === "/me") {
d.text = tokens.slice(1).join(" ")
sender.mode = "me"
}
addLine({time:d.T, sender:sender,
message:formatMessage(d.text)})
notify("chat")
break
case "quick":
addLine({time: d.T,
sender: sender,
message: d.symbol === undefined
? `[${d.text}]`
: `[${d.symbol}:${d.text}]`})
notify("quick")
if (d.cells !== undefined)
map.blink(d.cells, d.symbol)
break
case "name":
var oldName = aName(sender)
sender.name = d.name
sender.mode = "none"
addLine({time: d.T,
sender: sender,
message: [oldName, " теперь ", aName(sender), "."]})
break
case "map":
map.update(d.data, d.range)
break
case "map-reset":
mapSender.reset()
break
case "point":
points.put(d.coords)
break
case "join":
if (!window.bakaconf.hideJoinLeaveMessages)
addLine({time:d.T, message: [aName(sender), " заходит."]})
chat.setUsersCount(true, +1)
break
case "leave":
if (!window.bakaconf.hideJoinLeaveMessages)
addLine({time:d.T, message: [aName(sender), " выходит."]})
chat.setUsersCount(true, -1)
break
case "welcome":
welcomed = true
if (d.i !== undefined)
myId = d.i
break
case "ping":
d.t = 'pong'
send(d)
break
case "addr":
var connect = ""
showAddr({time:d.T, sender:sender}, d)
notify("chat")
break
case "addrs":
showAddrs(d.addrs, d.T)
break
case "restart":
addLine({time:d.T, message:["Сейчас сервер будет перезапущен"]})
break
}
}
function onmessage_binary(d) {
var c = 1
function getUint8 () { return d.getUint8 ((c += 1) - 1) }
function getUint16 () { return d.getUint16 ((c += 2) - 2) }
function getUint32 () { return d.getUint32 ((c += 4) - 4) }
function getFloat32() { return d.getFloat32((c += 4) - 4) }
function getString() {
var result = "", x = getUint16()
while (x !== 0) {
result += String.fromCharCode(x)
x = getUint16()
}
return result
}
function getColor() {
var col = getUint8()<<16 | getUint8()<<8 | getUint8()
col = col.toString(16)
while (col.length < 6)
col = "0" + col
return "#" + col
}
var data_size = getUint32()
var data = []
for (var i = 0; i < data_size; i++) {
var cell = {}
cell.i = getUint32()
cell.s = getUint32()
cell.x = getFloat32()
cell.y = getFloat32()
cell.c = getColor()
cell.n = getString()
var flags = getUint8()
cell.v = (flags & 1) !== 0
cell.a = (flags & 2) !== 0
data.push(cell)
}
var range_size = getUint32()
var range = []
for (var i = 0; i < range_size; i++) {
var r = {}
r.minX = getFloat32()
r.minY = getFloat32()
r.maxX = getFloat32()
r.maxY = getFloat32()
range.push(r)
}
map.update(data, range)
var aliveBakas = getUint8()
var totalBakas = getUint8()
if (totalBakas > 2)
labels.set('bakas', `${aliveBakas}/${totalBakas}`)
else
labels.set('bakas')
}
websocket = ws
}
function toggleCanvas() {
if (window.agar === undefined ||
window.agar.disableRendering === undefined)
return console.error("Could not find window.agar.disableRendering")
if (window.agar.disableRendering = !window.agar.disableRendering) {
document.body.setAttribute("baka-off", true)
fpsMeter.disable()
} else
chat.preserveScroll(() => document.body.removeAttribute("baka-off"))
}
function aButton(text, action, className, tooltip) {
var a = document.createElement('a')
if (className)
a.className = className
a.onclick = action
a.textContent = text
if (tooltip !== undefined)
a.title = tooltip
return a
}
function aWs(text, ws, region) {
return aButton(text, () => window.connect(ws, ''), 'Подключиться')
}
function aName(p) {
return aButton(p.name || defaultName, () => chat.clickName(),
"name" + (p.premium?" premium":""), p.i)
}
function formatMessage(text) {
var re = rx`( (? :noel: )
| (?: (?: ws://
| http://agar\.io/\?ip= )
(? [\w+.]+:[0-9]+\/? ))
| (? https?://[^\ ]*[^.\ ,\(\)] ))`
var result = splitReplace(text, re, function(m) {
var match = m[0], el = m[0]
if (m.noel) {
el = document.createElement("img")
el.src = "data: image/ gif; base64, R0lG"+
"O "+
"D lhEAAQALMAAP "+
"3 eAv7 pUv7 "+
"x mPW/ A+ "+
"C TA f8AALAAAP///8D Aw "+
"I CAgAAA AP///w "+
"A AA AAAAAAAAAAAACH5B AE "+
"A AA sALA AAAAAQ AB "+
"A AA ARec Mmpq p14VVN "+
"6 r9 lm "+
"j Jy nUAlirdy5qFWKIEd9a QM "+
"A rC ut qD lA "+
"o CI QW GY /R "+
"X Co CBibRwVhGa ha "+
"A zvVl LcCD "+
"H 7aX218ULq0 "+
"B "+
"PAMmJYgCenVoD1BSuFwUGbNQ2QwazN/EQA7";
el.title = el.alt = match
} else if (m.ws) {
el = aWs(match, 'ws://' + m.ws)
} else if (m.link) {
el = document.createElement('a')
el.textContent = el.href = match
el.target = "_blank"
}
return el
})
if (text.indexOf(g('nick').value||defaultName) > -1)
result.higlight = true
if (text.trim()[0] === '>')
result.greentext = true
return result
function rx(s) { return XRegExp(s.raw, 'x') }
function splitReplace(text, re, fun) {
var res = [], pos = 0
XRegExp.forEach(text, re, a => {
res.push(text.substring(pos, a.index))
res.push(fun(a))
pos = a.index + a[0].length
})
res.push(text.substring(pos))
return res
}
}
function br() {
return document.createElement('br')
}
function spinner() {
var span = document.createElement('span')
span.className = 'baka-spinner'
span.textContent = '☯'
return span
}
function addLine(p) {
var d = document.createElement('div')
d.className = 'baka-line'
if (p.time !== undefined) {
var time = document.createElement('span')
time.className = "time"
time.textContent = formatTime(p.time)
d.appendChild(time)
}
if (p.sender !== undefined) {
switch (p.sender.mode) {
case "message":
d.appendChild(aName(p.sender))
d.appendChild(document.createTextNode(": "))
break
case "me":
d.appendChild(document.createTextNode("•"))
d.appendChild(aName(p.sender))
d.appendChild(document.createTextNode(" "))
}
d.setAttribute("bakaid", p.sender.i)
}
if (p.message !== undefined) {
if (typeof p.message === "string")
p.message = [p.message]
var message = document.createElement('span')
p.message.forEach(i => {
if (!(i instanceof Node))
i = document.createTextNode(i)
message.appendChild(i)
})
message.className =
(p.message.greentext ? " greentext" : "") +
(p.message.higlight ? " higlight" : "")
d.appendChild(message)
}
chat.preserveScroll(() => g("msgsbox").appendChild(d))
}
function send(a) {
if (websocket.readyState === 1) {
if (!(a instanceof ArrayBuffer))
a = JSON.stringify(a)
websocket.send(a)
return true
}
return false
}
window.send = send
function sendAddr() {
var a = window.agar
if (a === undefined ||
a.ws === undefined ||
a.top === undefined ||
a.top.length === 0)
return
var m = {t: "addr", ws:a.ws, top:a.top, game:window.location.hostname}
if (a.region !== undefined) {
m.region = a.region
if (a.region.endsWith(":party"))
m.token =
document.getElementsByClassName("partyToken")[0]
.value.replace(/^agar\.io\//, "")
}
send(m)
}
var fullInfo = {
region(ws, fun) {
this._addCallback(data => fun(data.regions[ws]))
},
_request() {
this.req = new XMLHttpRequest()
this.req.open('GET', 'http://m.agar.io/fullInfo', true)
this.req.addEventListener('load', load)
this.req.addEventListener('error', error)
this.req.send()
console.log('get')
this.funs = []
function load() {
delete fullInfo.req
fullInfo.data = {regions:{}}
try {
JSON.parse(this.responseText).servers
.forEach(serv =>
fullInfo.data.regions[serv.ip] = serv.region)
} catch (e) {
error(e)
return
}
runFuns()
}
function error(e) {
delete fullInfo.req
delete fullInfo.data
runFuns()
console.error('fullInfo.startRequest', e)
}
function runFuns() {
fullInfo.funs.forEach(fun => fun(fullInfo.data))
fullInfo.funs = []
}
},
_addCallback(fun) {
if (this.req) {
this.funs.push(fun)
} else {
this._request()
this.funs.push(fun)
}
},
}
function joinTop(top) {
return top.map(x => x.name || "An unnamed cell").join(", ")
}
function showAddr(context, addr) {
var addrElem = aWs(addr.ws, addr.ws)
var regionElem = document.createElement('span')
var spinner_ = spinner()
context.message = [
`${addr.alive}/${addr.players} Топ: ${joinTop(addr.top)}`, br(),
addrElem, regionElem, spinner_,
]
addLine(context)
fullInfo.region(addr.ws.replace(/^wss?:\/\//, ''), update)
function update(region) {
spinner_.parentNode.removeChild(spinner_)
if (region !== undefined)
regionElem.textContent = ' ' + region
}
}
function showAddrs(addrs, time) {
addrs = addrs.filter(x => (x.alive || x.players > 2) && x.ws)
if (addrs.length === 0)
return addLine({time:time, message:["Сырны нигде не играют."]})
addLine({time:time, message:["Сырны играют тут:"]})
addrs.sort((x, y) => {
if (x.alive > y.alive) return +1
if (x.alive < y.alive) return -1
if (x.players > y.players) return +1
if (x.players < y.players) return -1
return 0
}).forEach(addr => showAddr({}, addr))
}
var submitHistory = {
list: [],
idx: -1,
text: "",
up() {
if (this.list.length === 0)
return
if (this.idx === -1) {
this.text = g('carea').value
g('carea').value = this.list[this.idx = this.list.length-1]
} else if (this.idx !== 0)
g('carea').value = this.list[--this.idx]
},
down() {
if (this.list.length === 0)
return
if (this.idx === this.list.length-1) {
this.idx = -1
g('carea').value = this.text
} else if (this.idx !== -1)
g('carea').value = this.list[++this.idx]
},
push(t) {
this.idx = -1
if (this.list[this.list.length -1] !== t)
this.list.push(t)
},
}
function submit(e) {
var ca = document.getElementById('carea')
function sendName() {
var n = document.getElementById('nick')
if (myName !== n.value) {
myName = n.value
storage.set('name', myName)
send({t: "name", name:myName})
}
}
if (ca.value !== "") {
var tokens = ca.value.trim().split(/ +/)
if (tokens.length === 0)
return false
if (tokens[0][0] === "/" && tokens[0] != "/me") {
switch(tokens[0]) {
case "/names":
send({t:"names"}); break
case "/addr":
sendName()
if (tokens[1] && window.agar)
window.agar.region = tokens[1]
sendAddr(); break
case "/addrs":
send({t:"addrs"}); break
case "/reconnect":
websocket.reconnect(); break
case "/ignore":
for (var i = 1; i < tokens.length; i++)
if (/^[-+]?\d+$/.test(tokens[i])) {
var id = parseInt(tokens[i].substr(1))
if (tokens[i][0] === '+')
ignore.add(id)
else
ignore.remove(id)
}
var chatmsg = ["Список игнорирования: ", ...join(ignore.list)];
addLine({message: chatmsg});
break
case "/auth":
if (tokens[1] !== undefined) {
storage.set("auth_token", tokens[1])
send({t:"auth", token:tokens[1]})
}
break
default:
["Команды чата:",
"/names — получить список сырн в чате",
"/addr [регион] — отправить текущий севрер и топ (требуется expose)",
"/addrs — получить список комнат, на которых играют сырны",
"/reconnect — переподключиться к чатсерверу",
"/ignore [действия] — работа со списком игнорирования. " +
"Пример: `/ignore +1 +3 -2` — добавить 1 и 3 в список и убрать 2 из списка",
"/auth — авторизация",
"/me — отправить сообщение от третьего лица",
].forEach(line => addLine({message:line}))
}
} else {
sendName()
send({t: "message", text: ca.value})
}
submitHistory.push(ca.value)
ca.value = ""
var myCells = agar.myCells()
if (myCells === undefined || myCells.length !== 0)
ca.blur()
}
return false
}
function codeWorkaround(e) {
// As of September 2015, `KeyboardEvent.code` is supported
// only in Firefox.
if (e.code !== undefined)
return
var k = e.keyCode
if (k >= 65 && k <= 90)
return e.code = `Key${String.fromCharCode(k)}`, undefined
if (k >= 48 && k <= 57)
return e.code = `Digit${k-48}`, undefined
e.code = {
"9": "Tab",
"13": "Enter",
"27": "Escape",
"188": "Comma",
"190": "Period",
"191": "Slash",
"192": "Backquote",
"220": "Backslash",
}[e.keyCode]
}
function handleEvents() {
// Autofire
var repeat = 0, repeatm = 0
window.setInterval(() => {
if (!repeat && !repeatm) return
if (!isHovered()) return repeat = repeatm = false
originalKeyEvent.w()
}, 50)
// Keyboard controls
var extended = false
window.addEventListener('keydown', (e) => {
codeWorkaround(e)
var nonText = /^(F[0-9]+)$/.test(e.code)
if (extended) {
if (quick.key(e) === false)
extended = false
stop(e)
return
}
if (chat.active) {
if (e.code === "Escape" || e.code === "Tab") {
chat.blur()
e.preventDefault()
}
e.stopPropagation()
g('carea').onkeydown(e)
return
}
var active = document.activeElement
if (active.id === 'baka-leaderboard-title') {
if (includes(["Escape", "Enter", "Tab"], e.code)) {
active.blur()
stop(e)
}
return
}
if (includes(["INPUT", "BUTTON", "SELECT"], active.tagName) &&
!nonText &&
active.offsetParent !== null) {
return
}
if (!e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.code === "KeyW") {
if (!e.shiftKey) {
e.stopPropagation()
repeat = 1
}
return
}
if (keys.keyDown(e)) {
stop(e)
return
}
if (quick.key(e)) {
extended = true
stop(e)
return
}
}
return
}, true)
window.addEventListener('keyup', (e) => {
codeWorkaround(e)
if (keys.keyUp(e))
stop(e)
if (e.code == "KeyW")
repeat = 0
}, true)
function stop(e) {
e.stopPropagation()
e.preventDefault()
}
// Mouse controls
function onMouseDown(e) {
if (e.which === 2 && window.agar && window.agar.scale !== undefined)
return window.agar.scale = 1, false
if (!window.bakaconf.mouseControls)
return true
switch (e.which) {
case 1: return repeatm = true, false
case 3: return originalKeyEvent.space(), false
}
}
function onMouseUp(e) {
if (e.which === 1) repeatm = false
}
;["canvas", "map", "baka-labels"]
.forEach(id => {
g(id).onmousedown = onMouseDown
g(id).oncontextmenu = (e) => !window.bakaconf.mouseControls
})
;["canvas", "map", "notification", "cbox", "baka-labels"]
.forEach(id => g(id).onmouseup = onMouseUp)
// Mouse controls: hover tracker
var hovered = {}
function isHovered() {
for (var i in hovered) if (hovered[i]) return true
return false
}
function track(id) {
function set(k, v) { hovered[k] = v }
g(id).addEventListener("mouseenter", () => set(id, true))
g(id).addEventListener("mousemove", () => set(id, true))
g(id).addEventListener("mouseleave", () => set(id, false))
}
var elements = ["canvas", "map", "cbox", "notification", "baka-labels"]
elements.forEach(track)
// Make baka UI mouse-transparent
elements.forEach(id => g(id).onmousemove = g("canvas").onmousemove)
;["canvas", "map", "notification", "baka-labels"]
.forEach(id => {
if (window.agar && window.agar.dommousescroll)
g(id).addEventListener('DOMMouseScroll',
window.agar.dommousescroll, false)
else
g(id).onmousewheel = document.body.onmousewheel
})
if (window.agar && window.agar.dommousescroll)
document.removeEventListener('DOMMouseScroll',
window.agar.dommousescroll, false)
else
document.body.onmousewheel = null
// Safety belt
window.onbeforeunload = safetyBelt
function safetyBelt(e) {
if (!e) e = window.event
e.returnValue = 'Выйти из agar.io?'
if (e.stopPropagation) {
e.stopPropagation()
e.preventDefault()
}
}
}
function handleOptions() {
g('nick').value = storage.get('name') || ''
var oldSetNick = window.setNick
window.setNick = (n) => {
if (n !== myName) {
myName = n
storage.set('name', myName)
send({t: "name", "name": myName})
}
oldSetNick(n)
}
var oldSetDarkTheme = window.setDarkTheme
window.setDarkTheme = (n) => {
canvas.dark = n
oldSetDarkTheme(n)
}
if (document
.querySelector('label input[onchange*=setDarkTheme]').checked)
window.setDarkTheme(true)
}
var agar = {
init() {
var a = window.agar
if (a === undefined)
return
if (a.minScale !== undefined)
a.minScale = 1/32
if (a.simpleCellDraw !== undefined)
a.simpleCellDraw = true
if (a.showStartupBg !== undefined)
a.showStartupBg = false
},
getViewport() {
var v = window.agar.rawViewport
if (v) {
var dx = 1024 / v.scale, dy = 600 / v.scale
return {minX:v.x-dx, minY:v.y-dy, maxX:v.x+dx, maxY:v.y+dy}
}
},
myCells() {
var a = window.agar
if (a === undefined || a.myCells === undefined ||
a.allCells === undefined)
return
return a.myCells.filter(x => x in a.allCells)
},
cellIsMy(cell) {
if (cell.baka_isMy === undefined)
cell.baka_isMy = includes(window.agar.myCells, cell.id)
return cell.baka_isMy
},
}
var dimensions = {
relative: null,
absolute: null,
shift: null,
reset() {
this.relative = this.shift = null
},
update(minX, minY, maxX, maxY) {
var w = maxX - minX, h = maxY - minY
if (w < 6000 && h < 3400)
return
var s = {x: (minX + maxX) / 2,
y: (minY + maxY) / 2}
this.relative = [minX, minY, maxX, maxY]
this.absolute = [minX-s.x, minY-s.y, maxX-s.x, maxY-s.y]
this.shift = s
},
getRelative() {
return this.relative || window.agar.dimensions
},
getAbsolute() {
return this.absolute || window.agar.dimensions
},
getShift() {
return this.shift || {x:0, y:0}
},
}
var map = {
canvas: null,
data: [],
range: [],
blackRibbon: true,
hidden: false,
blinks: {},
blinkIdsCounter: 0,
init() {
this.canvas = document.createElement("canvas")
this.canvas.id = "map"
document.body.appendChild(this.canvas)
this.canvas.onclick =
() => { this.blackRibbon = false; this.draw() }
this.draw()
},
toggle() {
this.hidden = !this.hidden
g('map').style.visibility = (this.hidden ? 'hidden' : '')
},
move() { toggleAttribute(this.canvas, 'data-alt-position') },
update(data, range) {
mapSender.waitReply = false
this.data = data
this.range = range
bakaSkin.updateIds(data)
this.draw()
},
blink(ids, sym) {
var c = this.blinkIdsCounter++
var iteration = 12
var blink = this.blinks[c] = {ids:ids, sym:sym, hl:false}
function toggle() {
if (--iteration)
window.setTimeout(toggle, 250)
else
delete map.blinks[c]
blink.hl = !blink.hl
map.draw()
}
toggle()
},
draw() {
if (this.hidden)
return
var size = this.canvas.width = this.canvas.height =
window.bakaconf.mapSize
var context = this.canvas.getContext('2d')
var myCells = agar.myCells() || []
var teams = window.bakaconf.teams
var idx = {}
function getAura(cell) {
if (includes(myCells, cell.i))
return window.bakaconf.myAura
if (cell.a)
return window.bakaconf.bakaAura
if (window.bakaconf.showOnlyBakaAura)
return
for (var i in teams) {
var names = teams[i].names
if (!isArray(names))
names = [names]
for (var j = 0; j < names.length; j++)
if (names[j] === cell.n ||
(names[j].test && names[j].test(cell.n)))
return teams[i].aura || window.bakaconf.defaultTeamAura
}
}
context.clearRect(0, 0, this.canvas.width, this.canvas.height)
var t = getTransform()
var i
function getTransform() {
var d = dimensions.getAbsolute()
var x0 = d[0], y0 = d[1], x1 = d[2], y1 = d[3]
var w = map.canvas.width, h = map.canvas.height
var mx = w / (x1-x0), bx = -mx * x0
var my = w / (x1-x0), by = -my * y0
return {
x: v => mx*v + bx,
y: v => my*v + by,
s: v => v * mx,
}
}
if (window.bakaconf.fogOfWar) {
context.globalAlpha = 0.3
context.fillStyle = "#000"
context.fillRect(0, 0, this.canvas.width, this.canvas.height)
context.globalAlpha = 0.7
context.fillStyle = "#777"
context.beginPath()
for (i = 0; i < this.range.length; i++) {
var d = this.range[i]
context.rect(t.x(d.minX), t.y(d.minY),
t.s(d.maxX-d.minX),
t.s(d.maxY-d.minY))
}
context.fill()
} else {
context.globalAlpha = 0.5
context.fillStyle = "#777"
context.fillRect(0, 0, this.canvas.width, this.canvas.height)
}
if (this.blackRibbon) {
context.beginPath()
context.lineWidth = size/8
context.strokeStyle = "#000"
context.beginPath();
context.moveTo(size/2, size*9/8);
context.lineTo(size*9/8, size/2);
context.stroke()
context.fill()
}
context.globalAlpha = .4
for (i = 0; i < this.data.length; i++) {
var d = this.data[i]
var aura = getAura(d)
if (aura) {
context.fillStyle = aura
context.beginPath()
context.arc(t.x(d.x), t.y(d.y), t.s(d.s)+4,
0, 2 * Math.PI, false)
context.fill()
}
idx[d.i] = this.data[i]
}
context.lineWidth = 2
for (i = 0; i < this.data.length; i++) {
var d = this.data[i]
context.beginPath()
context.arc(t.x(d.x), t.y(d.y), t.s(d.s),
0, 2 * Math.PI, false)
context.globalAlpha = 1
context.fillStyle = d.c
context.fill()
if (d.v) {
context.strokeStyle = "#ff0000"
} else {
context.globalAlpha = .1
context.strokeStyle = "#000000"
}
context.stroke()
}
for (var i in this.blinks) {
var blink = this.blinks[i]
if (blink.ids.length === 0 || !blink.hl)
continue
context.beginPath()
context.fillStyle = "#f00"
context.globalAlpha = 0.7
var minX, maxX, minY, maxY, drawText = false
for (var j = 0; j < blink.ids.length; j++) {
var d = idx[blink.ids[j]]
if (d === undefined) continue
drawText = true
if (j === 0 || minX > d.x*2-d.s) minX = d.x*2-d.s
if (j === 0 || maxX < d.x*2+d.s) maxX = d.x*2+d.s
if (j === 0 || minY > d.y*2-d.s) minY = d.y*2-d.s
if (j === 0 || maxY < d.y*2+d.s) maxY = d.y*2+d.s
context.arc(t.x(d.x), t.y(d.y), t.s(d.s)+6,
0, 2 * Math.PI, false)
context.closePath()
}
context.fill()
if (!drawText || blink.sym === undefined) continue
context.globalAlpha = 1
context.font = 'bold 13px Ubuntu'
context.textAlign = 'center'
context.textBaseline = 'middle'
context.fillStyle = '#0ff'
context.fillText(blink.sym, t.x((minX+maxX)/4), t.y((minY+maxY)/4))
}
},
}
var mapSender = {
waitReply: false,
buffer: new ArrayBuffer(1024),
init() {
this.reset()
window.setInterval(() => this.send(), 250)
},
reset() {
this.waitReply = false
this.sent = {}
this.sent.topNames = new Map
if (window.agar && window.agar.allCells)
for (var id in window.agar.allCells)
window.agar.allCells[id].baka_old = undefined
},
send() {
var a = window.agar
var myCells = agar.myCells()
if (!checkExpose()) {
if (!map.hidden)
send({t:'map', reply:1})
return
}
if (!a.top.length)
return
var offset = 0
var d = new DataView(this.buffer)
var reply = !map.hidden && !this.waitReply
putAll()
var sent = send(truncateBuffer())
if (sent && reply)
this.waitReply = true
if (!sent)
this.reset()
map.blackRibbon = false
function checkExpose() {
return a !== undefined &&
a.allCells !== undefined &&
myCells !== undefined &&
a.top !== undefined &&
a.ws
}
function putAll() {
var flagsOffset = reserve(1)
var flags
var dimsShift = dimensions.getShift()
flags |= !mapSender.waitReply << 0
flags |= !map.hidden << 1
flags |= putGame() << 2
flags |= putTop() << 3
putAllCells(dimsShift)
putViewport(dimsShift)
d.setUint8(flagsOffset, flags)
}
function putGame() {
var s = mapSender.sent
if (s.ws === a.ws && s.hostname === window.location.hostname)
return false
putString(s.ws = a.ws)
putString(s.hostname = window.location.hostname)
return true
}
function putTop() {
if (topsIsSame(a.top, mapSender.sent.top, false))
return false
var infoOffset = reserve(2)
var top = a.top.slice(0, 10)
var info = top.length
var shift = 4
var sentTopNames = new Map
for (var e of top) {
putUint32(e.id)
if (mapSender.sent.topNames.get(e.id) !== e.name) {
putString(e.name)
info |= 1 << shift
}
shift++
sentTopNames.set(e.id, e.name)
}
d.setUint16(infoOffset, info)
mapSender.sent.topNames = sentTopNames
mapSender.sent.top = top
return true
}
function topsIsSame(a, b) {
if (a === b)
return true
if (!a || !b || a.length != b.length)
return false
for (var i = 0; i < a.length; i++)
if (a[i].id !== b[i].id || a[i].name !== b[i].name)
return false
return true
}
function putAllCells(shift) {
var prevId = 0
var ids = Object.keys(a.allCells).map(x=>+x).sort((x,y) => x-y)
for (var id of ids) {
var cell = a.allCells[id]
agar.cellIsMy(cell)
if (cell.size < 32 && !cell.baka_isMy)
continue
var flagsOffset = reserve(1)
var flags = 1
flags |= cell.isVirus << 1
flags |= cell.baka_isMy << 2
putUint(id - prevId)
prevId = id
var p = window.agar.cellProp
var old = cell.baka_old
if (typeof old === 'undefined')
old = cell.baka_old = {}
if (old.s !== cell[p.nSize]) {
putUint16(old.s = cell[p.nSize])
flags |= 1 << 3
}
var x = cell[p.nx] - shift.x, y = cell[p.ny] - shift.y
if (old.x !== x || old.y !== y) {
putInt16(old.x = x)
putInt16(old.y = y)
flags |= 1 << 4
}
if (old.n !== cell.name || old.c !== cell.color) {
putColor(old.c = cell.color)
putString(old.n = cell.name)
flags |= 1 << 5
}
d.setUint8(flagsOffset, flags)
}
putUint8(0)
}
function putColor(value) {
putUint8(parseInt(value.substr(1, 2), 16))
putUint8(parseInt(value.substr(3, 2), 16))
putUint8(parseInt(value.substr(5, 2), 16))
}
function putViewport(shift) {
var viewport = agar.getViewport()
putFloat32(viewport.minX - shift.x)
putFloat32(viewport.minY - shift.y)
putFloat32(viewport.maxX - shift.x)
putFloat32(viewport.maxY - shift.y)
}
function putUint8 (value) { d.setUint8 (reserve(1), value) }
function putUint16 (value) { d.setUint16 (reserve(2), value) }
function putUint32 (value) { d.setUint32 (reserve(4), value) }
function putInt16 (value) { d.setInt16 (reserve(2), value) }
function putFloat32(value) { d.setFloat32(reserve(4), value) }
function putString (str) {
var offset = reserve(str.length*2 + 2)
for (var i = 0; i <= str.length; i++)
d.setUint16(offset + i*2, str.charCodeAt(i) || 0)
}
function putUint(value) {
do {
var v = 0x7f & value
var hasNext = (value ^ v) !== 0
putUint8(v | hasNext << 7)
value >>>= 7
} while (value)
}
function reserve(count) {
if (offset + count + 4 > mapSender.buffer.byteLength)
enlargeBuffer()
offset += count
return offset - count
}
function enlargeBuffer() {
var newBuf = new ArrayBuffer(mapSender.buffer.byteLength*2)
copyBufferInto(newBuf)
d = new DataView(mapSender.buffer = newBuf)
}
function truncateBuffer() {
var result = new ArrayBuffer(offset)
copyBufferInto(result)
return result
}
function copyBufferInto(to) {
var from = new Uint8Array(mapSender.buffer, 0, offset)
to = new Uint8Array(to)
to.set(from)
}
},
}
var bakaSkin = {
cached: {my:null, baka:null},
byAttr: new Map(),
init() {
if (!window.agar)
return
this.image('my')
this.image('baka')
},
updateIds(mapCells) {
if (!window.agar || !window.agar.allCells)
return
this.byAttr.clear()
mapCells.forEach(c => {
if (!c.a)
return
var o = window.agar.allCells[c.i]
if (o)
o.baka_isBaka = true
if (c.n !== "")
this.byAttr.set(c.c + c.n, c.a)
})
},
image(id) {
var uri = window.bakaconf[id + 'SkinUri']
if (!uri)
return this.cached[id] = null
if (this.cached[id] !== null && this.cached[id].src === uri) {
this.cached[id].big = window.bakaconf[id + 'SkinBig']
return this.cached[id]
}
this.cached[id] = new Image()
if (!(/^\s*data\s*:/i).test(uri))
this.cached[id].crossOrigin = 'anonymous'
this.cached[id].src = uri
this.cached[id].big = window.bakaconf[id + 'SkinBig']
return this.cached[id]
},
handleCell(cell) {
if (cell.baka_isBaka === undefined)
cell.baka_isBaka = !!this.byAttr.get(cell.color + cell.name)
agar.cellIsMy(cell)
cell.baka_skin =
cell.baka_isMy ? this.image('my') :
cell.baka_isBaka ? this.image('baka') :
undefined
cell.baka_color =
cell.size < 20 ? window.bakaconf.pelletColor :
cell.isVirus ? window.bakaconf.virusColor :
undefined
},
}
function Point(coords) {
var start = Date.now()
this.draw = () => {
var t = Date.now() - start
for (var i = 0; i < 3; i++)
drawCircle(t - 200*i)
return t <= 1400
}
function drawCircle(t) {
if (t < 0 || t > 1000)
return
canvas.ctx.globalAlpha = 1 - t/1000
var rad = 200000/(2000-t)-200000/2000
var width = 1 + t/100
canvas.ctx.beginPath()
canvas.ctx.lineWidth = width / window.agar.drawScale
canvas.ctx.arc(coords.x, coords.y,
rad / window.agar.drawScale,
0, 2*Math.PI)
canvas.ctx.stroke()
}
}
var points = {
points: new Set(),
send() {
send({t:'point', coords:canvas.toWorldCoords(mouseLines.cursor)})
},
put(coords) {
this.points.add(new Point(coords))
},
draw() {
canvas.ctx.globalCompositeOperation = "difference"
canvas.ctx.strokeStyle = "white"
for (var point of this.points)
if (!point.draw())
this.points.delete(point)
canvas.ctx.globalAlpha = 1
canvas.ctx.globalCompositeOperation = "source-over"
},
}
var canvas = {
transform: {scale:1, x:0, y:0},
dark: false,
init() {
this.element = g('canvas')
this.ctx = g('canvas').getContext('2d')
},
drawRectangle(dim, color, width) {
this.ctx.beginPath()
this.ctx.strokeStyle = color
this.ctx.lineWidth = width
this.ctx.strokeRect(dim.minX - width/2,
dim.minY - width/2,
dim.maxX - dim.minX + width,
dim.maxY - dim.minY + width)
},
toWorldCoords(pixelCoords) {
var x = (pixelCoords.x - this.transform.x) / this.transform.scale
var y = (pixelCoords.y - this.transform.y) / this.transform.scale
return {x, y}
}
}
var hooks = {
init() {
if (!window.agar || !window.agar.hooks)
return
var prefix = "hook_"
Object.keys(this)
.filter(name => name.startsWith(prefix))
.forEach(name =>
window.agar.hooks[name.substr(prefix.length)] =
this[name].bind(this))
},
hook_cellSkin(cell, prev) {
return cell.baka_skin || prev
},
hook_cellColor(cell) {
if (cell.size >= 32)
canvas.ctx.globalAlpha = window.bakaconf.cellOpacity
bakaSkin.handleCell(cell)
return cell.baka_color
},
hook_beforeTransform(ctx, t1x, t1y, s, t2x, t2y) {
canvas.ctx.globalAlpha = 1
fpsMeter.tick()
canvas.transform = {scale:s, x:t2x*s + t1x, y:t2y*s + t1y}
if (zc)
return
bgImage.draw.apply(bgImage, arguments)
},
hook_beforeDraw() {
if (zc)
return
if (window.agar && window.agar.webSocket === null)
return
var dims, box
if ((box = window.bakaconf.viewportBox) &&
(dims = agar.getViewport()))
canvas.drawRectangle(dims, box.color, box.width)
if ((box = window.bakaconf.worldBox) &&
(dims = dimensions.getRelative())) {
dims = {minX:dims[0], minY:dims[1], maxX:dims[2], maxY:dims[3]}
canvas.drawRectangle(dims, box.color, box.width)
}
activeCell.calculate()
eatingDistanceGuide.calculate()
mouseLines.draw()
},
hook_afterDraw() {
points.draw()
},
hook_afterCellStroke(cell) {
if (zc || cell.size < 32)
return
eatingMassGuide.draw(canvas.ctx, cell)
activeCell.draw(canvas.ctx, cell)
eatingDistanceGuide.draw(canvas.ctx, cell)
},
hook_skipCellDraw(cell) {
return !drawPellets &&
(cell.size < 37 || cell.name !== '' && cell.size < 43)
},
hook_drawCellMass(cell, prev) {
if (cell.isVirus)
return true
if (cell.size < 32)
return false
var fontSize = Math.max(~~(.3 * cell.size), 24) *
window.agar.drawScale
return fontSize > 10.5
},
hook_cellMassText(cell, mass) {
return cell.isVirus
? shotsNeeded()
: `${mass}${shotsAvailable()}${percent()}`
function shotsAvailable() {
if (!includes(window.agar.myCells, cell.id))
return ""
var count = ~~((mass-17)/18)
if (count > 7*3)
return ""
return ` (${count})`
}
function shotsNeeded() {
return Math.floor((149-cell.size)/7)
}
function percent() {
if (activeCell.cell === null)
return ""
var proportions = cell.size / activeCell.cell.size
var pct = ~~(100*proportions*proportions)
return ` ${pct}%`
}
},
hook_cellMassTextScale(cell, scale) {
return cell.isVirus
? 100
: scale * 1.5
},
hook_drawScore(mass) {
massMeter.update(mass)
return true
},
hook_updateLeaderboard() {
leaderboard.update()
return true
},
hook_connect() {
dimensions.reset()
},
hook_dimensionsUpdated(minX, minY, maxX, maxY) {
dimensions.update(minX, minY, maxX, maxY)
},
}
var bgImage = {
init() {
this.img = new Image()
this.img.crossOrigin = 'anonymous'
},
draw(ctx, t1x, t1y, s, t2x, t2y) {
if (!window.bakaconf.bgImage)
return
if (this.img.src !== window.bakaconf.bgImage)
this.img.src = window.bakaconf.bgImage
if (!this.img.complete || !this.img.naturalWidth)
return
var bgDims = fitDimensions(this.img, ctx.canvas)
ctx.save()
clip()
ctx.drawImage(this.img, ...bgDims)
ctx.restore()
function fitDimensions(bounds, d) {
var useWidth = bounds.width * d.height < bounds.height * d.width
var width = useWidth
? d.width
: bounds.width * d.height / bounds.height
var height = !useWidth
? d.height
: bounds.height * d.width / bounds.width
return [(d.width - width) / 2,
(d.height - height) / 2,
width, height]
}
function clip() {
var mapDims, gameDims
ctx.beginPath()
if (!map.hidden)
rect(mapDims = getMapDims())
if (window.agar.webSocket !== null)
rect(gameDims = getGameDims())
if (mapDims && gameDims)
rectIntersection(gameDims, mapDims)
ctx.rect(...bgDims)
ctx.clip("evenodd")
}
function rectIntersection(a, b) {
var r = [Math.max(a[0], b[0]), Math.max(a[1], b[1]),
Math.min(a[2], b[2]), Math.min(a[3], b[3])]
if (r[0] < r[2] && r[1] < r[3])
rect(r)
}
function getMapDims() {
var d = map.canvas.getBoundingClientRect()
return [d.left, d.top, d.right, d.bottom]
}
function getGameDims() {
var d = dimensions.getRelative()
return [(d[0]+t2x)*s+t1x, (d[1]+t2y)*s+t1y,
(d[2]+t2x)*s+t1x, (d[3]+t2y)*s+t1y]
}
function rect(d) {
ctx.rect(d[0], d[1], d[2]-d[0], d[3]-d[1])
}
},
}
var activeCell = {
cell: null,
drawEnabled: true,
splitGuideEnabled: true,
activate(num) {
var cells = agar.myCells()
.map(id => window.agar.allCells[id])
.sort((x,y) => y.size - x.size)
if (num === undefined) {
num = cells.indexOf(this.cell)
num = num === -1 ? 0 : (num+1) % cells.length
}
this.cell =
cells.length === 0 ?
null :
cells[Math.min(num, cells.length-1)]
},
calculate() {
if (!this.cell || !window.agar.allCells[this.cell.id])
this.activate(0)
},
toggleDraw() { this.drawEnabled = !this.drawEnabled },
toggleSplitGuide() { this.splitGuideEnabled = !this.splitGuideEnabled },
draw(ctx, cell) {
if (this.cell !== cell)
return
var scale = window.agar.drawScale
if (this.drawEnabled)
drawAura('#3371FF', 5/scale, cell.size + 10 + 10/scale)
if (this.splitGuideEnabled) {
drawAura('#00FF00', 2, 660)
drawAura('#FF0000', 2, 660 + cell.size)
}
function drawAura(color, width, radius) {
ctx.strokeStyle = color
ctx.lineWidth = width
ctx.beginPath()
ctx.arc(cell.x, cell.y, radius, 0, 2*Math.PI)
ctx.stroke()
}
},
}
var eatingMassGuide = {
enabled: true,
splitCount(cellA, cellB) {
var coef = 3/4
var k = coef * (cellA.size*cellA.size)/(cellB.size*cellB.size)
var raw = Math.log2(k)+1
var count = Math.floor(raw)
var progress = k / Math.pow(2,count-1) - 1
return {count, progress}
},
toggle() { this.enabled = !this.enabled },
draw(ctx, cell) {
if (!this.enabled
|| activeCell.cell === null
|| ~window.agar.myCells.indexOf(cell.id))
return
var conf = window.bakaconf.eatingMassGuide
var scale = conf.width / window.agar.drawScale
var splits, colors
if (activeCell.cell.size > cell.size || cell.isVirus) {
splits = this.splitCount(activeCell.cell, cell)
colors = conf.smallColors
} else {
splits = this.splitCount(cell, activeCell.cell)
colors = conf.bigColors
}
var color = colors[splits.count], colorNext = colors[splits.count+1]
if (!colorNext)
ctx.globalAlpha = 1 - splits.progress/2
if (color) {
ctx.lineWidth = 10 * scale
ctx.strokeStyle = color
ctx.beginPath()
ctx.arc(cell.x, cell.y,
cell.size + 10 + 10*scale,
0, 2*Math.PI)
ctx.stroke()
}
if (colorNext || color) {
var angle = Math.atan2(cell.y - activeCell.cell.y,
cell.x - activeCell.cell.x)
var angleW = Math.PI * splits.progress
ctx.lineWidth = 3 * scale
ctx.setLineDash([ctx.lineWidth, ctx.lineWidth])
ctx.strokeStyle = colorNext || color
ctx.beginPath()
ctx.arc(cell.x, cell.y,
cell.size + 10 + 18*scale,
angle-angleW, angle+angleW)
ctx.stroke()
ctx.setLineDash([])
}
ctx.globalAlpha = 1
},
}
var eatingDistanceGuide = {
enabled: true,
toggle() { this.enabled = !this.enabled },
calculate() {
var eaten = window.agar.eatenCellsList
var alive = window.agar.aliveCellsList
if (!this.enabled || zc || !eaten || !alive)
return
eaten.forEach(reset)
alive.forEach(reset)
var bigEnough = alive.filter(o => o.size > 32)
handleCells(bigEnough)
function reset(o) {
o.baka_eatees = []
o.baka_eater = undefined
o.baka_progress = Number.POSITIVE_INFINITY
}
function handleCells(cells) {
cells
.filter(eater => !eater.isVirus)
.forEach(eater =>
cells.forEach(eatee => handlePair(eater, eatee)))
}
function handlePair(eater, eatee) {
if (eater.size <= eatee.size)
return
var dist_min = eater.size - 0.354*eatee.size + 11
var dist_max = eater.size + eatee.size
var dist = calcDist()
if (dist >= dist_max)
return
var progress = (dist - dist_min) / (dist_max - dist_min)
eater.baka_eatees.push({
cell: eatee,
radius: dist_min,
angle: Math.atan2(eatee.y-eater.y, eatee.x-eater.x),
progress: progress,
max_angle: Math.atan2(eatee.size, dist_max),
})
if (eatee.baka_eater === undefined ||
eatee.baka_progress > progress) {
eatee.baka_eater = eater
eatee.baka_progress = progress
}
function calcDist() {
var dx = eater.x - eatee.x, dy = eater.y - eatee.y
return Math.sqrt(dx*dx + dy*dy)
}
}
},
draw(ctx, cell) {
if (!this.enabled || !cell.baka_eatees)
return
ctx.strokeStyle = "#000000"
ctx.fillStyle = "#000000"
cell.baka_eatees.forEach(handleEatee)
ctx.globalAlpha = 1
function handleEatee(eatee) {
ctx.globalAlpha = 1 - eatee.progress
drawProgressArc()
if (eatee.progress < 0)
drawLine()
if (cell === eatee.cell.baka_eater)
drawDotInCenterOf(eatee.cell)
function drawProgressArc() {
var angle = Math.max(0.2, eatee.progress) * eatee.max_angle
ctx.beginPath()
ctx.arc(cell.x, cell.y,
eatee.radius,
eatee.angle-angle, eatee.angle+angle)
ctx.stroke()
}
function drawLine() {
ctx.lineWidth = 10
ctx.beginPath()
ctx.moveTo(cell.x + eatee.radius*Math.cos(eatee.angle),
cell.y + eatee.radius*Math.sin(eatee.angle))
ctx.lineTo(eatee.cell.x, eatee.cell.y)
ctx.stroke()
}
function drawDotInCenterOf(cell) {
ctx.beginPath()
ctx.arc(cell.x, cell.y, 20, 0, 2 * Math.PI)
ctx.fill()
}
}
},
}
var direction = {
stopped: false,
toggleStop() {
this.stopped = !this.stopped
window.agar.enableDirectionSending = !this.stopped
if (this.stopped)
this.send(window.agar.rawViewport.x,
window.agar.rawViewport.y)
},
send(x, y) {
var ws = window.agar.webSocket
if (ws === null || ws.readyState !== ws.OPEN)
return
var v = new DataView(new ArrayBuffer(13))
v.setUint8(0, 16)
v.setInt32(1, x, true)
v.setInt32(5, y, true)
v.setUint32(9, 0, true)
ws.send(v.buffer)
},
}
function multiSplit(count) {
for (var i = 0; i < count; i++)
window.setTimeout(() => originalKeyEvent.space(), i*50)
}
var originalKeyEvent = {
esc() { this._(27) },
space() { this._(32) },
q() { this._(81) },
w() { this._(87) },
_(keyCode) {
var e = {keyCode,
target: canvas.element,
preventDefault: () => undefined}
if (window.onkeydown)
window.onkeydown(e)
if (window.onkeyup)
window.onkeyup(e)
},
}
var mouseLines = {
enabled: false,
cursor: {x:0, y:0},
showCursor: true,
init() {
if (!window.agar)
this.draw = () => {}
document.addEventListener("mousemove", e => {
this.cursor.x = e.clientX
this.cursor.y = e.clientY
})
},
toggle() { this.enabled = !this.enabled },
draw() {
var that = this
if (!this.enabled)
return updateCursor(true)
var myCells = agar.myCells().map(id => window.agar.allCells[id])
if (myCells.length === 0)
return updateCursor(true)
var coords = canvas.toWorldCoords(this.cursor)
updateCursor(false)
draw(canvas.ctx)
function updateCursor(showCursor) {
if (!window.bakaconf.replaceCursor)
showCursor = true
if (showCursor === that.showCursor)
return
that.showCursor = showCursor
canvas.element.style.cursor = map.canvas.style.cursor =
showCursor ? '' : 'none'
}
function draw(ctx) {
var scale = window.agar.drawScale
ctx.strokeStyle = ctx.fillStyle = canvas.dark ? "#fff" : '#000'
ctx.lineWidth = 3/scale
ctx.lineCap = "round"
ctx.globalAlpha = .5
ctx.beginPath()
for (var cell of myCells) {
ctx.moveTo(cell.x, cell.y)
ctx.lineTo(coords.x, coords.y)
}
ctx.stroke()
if (window.bakaconf.replaceCursor) {
ctx.beginPath()
ctx.arc(coords.x, coords.y, 10/scale, 0, 2*Math.PI, false)
ctx.fill()
}
ctx.globalAlpha = 1
}
}
}
var labels = {
lines: {},
visibleLinesCount: 0,
init() {
this.element = document.createElement('div')
this.element.id = 'baka-labels'
this.element.style.display = 'none'
document.body.appendChild(this.element)
addLine('bakas', 'Дуры')
addLine('mass', 'Масса')
addLine('cells', 'Шары')
addLine('fps', 'FPS')
function addLine(lineName, text) {
var div = document.createElement('div')
div.style.display = 'none'
div.appendChild(document.createTextNode(text + ': '))
var span = document.createElement('span')
div.appendChild(span)
labels.element.appendChild(div)
labels.lines[lineName] = {div, span}
}
},
set(lineName, value) {
var line = this.lines[lineName]
if (!line)
return
line.span.textContent = value
if (line.div.style.display === 'none' && value !== undefined) {
line.div.style.display = ''
++this.visibleLinesCount
this.element.style.display = ''
} else if (line.div.style.display === '' && value === undefined) {
line.div.style.display = 'none'
if (--this.visibleLinesCount === 0)
this.element.style.display = 'none'
}
},
}
var fpsMeter = {
frames: 1,
start: Date.now(),
disable() {
labels.set('fps')
},
tick() {
var now = Date.now()
if (now - this.start > 1000) {
labels.set('fps', ~~(this.frames / (now-this.start) * 10000)/10)
this.start = now
this.frames = 1
} else {
this.frames++
}
},
}
var massMeter = {
max: 0,
update(mass) {
if (mass === 0) {
this.max = 0
labels.set('mass')
labels.set('cells')
} else {
mass = ~~(mass/100)
this.max = Math.max(mass, this.max)
labels.set('mass', `${mass}/${this.max}`)
labels.set('cells', agar.myCells().length)
}
},
}
var leaderboard = {
initialized: false,
init() {
if (this.initialized)
return
this.initialized = true
var div = document.createElement('div')
div.id = 'baka-leaderboard'
document.body.appendChild(div)
var title = document.createElement('input')
title.id = 'baka-leaderboard-title'
title.value = storage.get('leaderboard-title') || 'Leaderboard'
title.oninput = () => storage.set('leaderboard-title', title.value)
title.maxLength = 40
div.appendChild(title)
var items = document.createElement('ol')
items.id = 'baka-leaderboard-items'
div.appendChild(items)
div.appendChild(document.createElement('span'))
this.title = title
this.items = items
},
setItems(items) {
ensureThereAreNNodes(items.length)
for(var i = 0; i < items.length; i++) {
var item = leaderboard.items.childNodes[i]
var isMe = includes(window.agar.myCells.concat(1), items[i].id)
item.className = isMe ? 'baka-leaderboard-me' : ''
item.textContent = items[i].name || 'An unnamed cell'
}
function ensureThereAreNNodes(n) {
var items = leaderboard.items
var childs = items.childNodes.length
if (childs < n)
for (var i = childs; i < n; i++)
items.appendChild(document.createElement('li'))
else if (childs > n)
for (var i = childs - n; i > 0; i--)
items.removeChild(items.lastChild)
}
},
update() {
this.init()
this.setItems(window.agar.top)
},
}
function toggleFullscreen() {
if (document.mozFullScreen == false)
document.body.mozRequestFullScreen()
else if (document.mozFullScreen == true)
document.mozCancelFullScreen()
else
return false
}
function keyToStr(e) {
var mod = e.shiftKey || e.altKey || e.ctrlKey || e.metaKey
var key1 = mod ? "S" : "_"
if (e.keyCode < 10) key1 += "00" + e.keyCode
else if (e.keyCode < 100) key1 += "0" + e.keyCode
else key1 += e.keyCode
var key2 = (mod ? `Shift_${e.code}` : e.code)
return [key1, key2]
}
var keys = {
actionsDown: {
toggle_eating_mass_guide() { eatingMassGuide.toggle() },
toggle_eating_distance_guide() { eatingDistanceGuide.toggle() },
toggle_active_cell() { activeCell.toggleDraw() },
toggle_split_guide() { activeCell.toggleSplitGuide() },
toggle_mouse_lines() { mouseLines.toggle() },
toggle_map() { map.toggle() },
toggle_chat() { chat.toggle() },
toggle_canvas() { toggleCanvas() },
toggle_pellets() { drawPellets = !drawPellets },
focus_chat() { chat.focus() },
move_chat() { chat.move() },
move_map() { map.move() },
activate_cell(n) { activeCell.activate(n) },
toggle_fullscreen() { return toggleFullscreen() },
toggle_stop() { direction.toggleStop() },
double_split() { multiSplit(2) },
quadruple_split() { points.send() },
toggle_skins() { g('noSkins').click() },
toggle_names() { g('noNames').click() },
toggle_mass() { g('showMass').click() },
},
actionsUp: {
quadruple_split() { multiSplit(4) },
},
keyDown(e) {
if (e.repeat)
return true
return this.key(e, this.actionsDown)
},
keyUp(e) { return this.key(e, this.actionsUp) },
key(e, actions) {
var conf = window.bakaconf.keys
var action = conf[keyToStr(e).find(e => conf[e])]
if (action === undefined)
return false
var result = true
if (typeof action === 'string')
executeAction(action)
else if (isArray(action))
action.forEach(executeAction)
return result
function executeAction(str) {
var args = str.trim().split(/ +/)
var action = actions[args.shift()]
if (action === undefined)
return
if (action(...args.map(JSON.parse)) === false)
result = false
}
},
}
var quick = {
state: undefined,
key(e, state) {
if (e.keyCode >= 16 && e.keyCode <= 18 && e.keyCode) return
var keys = keyToStr(e)
var show = false
if (this.state === undefined) {
show = true
this.state = window.bakaconf.quickTemplates
}
this.state = this.state[keys[0]] || this.state[keys[1]]
if (this.state === undefined || isArray(this.state)) {
if (this.state)
send({t:"quick", symbol:this.state[0], text:this.state[1],
cells:agar.myCells()})
this.state = undefined
this.hide()
return false
}
if (show)
this.show()
return true
},
show() {
this.hide()
var quickHint = document.createElement('div')
quickHint.id = "quickHint"
function codeToName(x) {
if (x == "Backquote") return "`"
if (x.startsWith("Key")) return x.substr(3)
return x
}
function keyCodeToName(x) {
if (x == 192) return "`"
if (x >= 48 && x <= 57) return ""+(x-48)
if (x >= 96 && x <= 105) return "num"+(x-96)
if (x >= 65 && x <= 90) return String.fromCharCode(x)
return `[${x}]`
}
function list(prefix, what) {
var keys = Object.keys(what)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var keyName = ""
if (/^[_S][0-9]{3}$/.test(key))
keyName = (key[0] === '_'?'':'⇧') +
keyCodeToName(+key.substr(1))
else
keyName = key.startsWith('Shift_')
? '⇧' + codeToName(key.substr(6))
: codeToName(key)
if (isArray(what[key])) {
var line = document.createElement('div')
function span(className, textContent) {
var span = document.createElement('span')
span.textContent = textContent
span.className = className
line.appendChild(span)
}
span('key', prefix + keyName)
span('sym', what[key][0])
span('text', what[key][1])
quickHint.appendChild(line)
} else {
list(prefix + keyName + " ", what[key])
}
}
}
list("", window.bakaconf.quickTemplates)
document.body.appendChild(quickHint)
},
hide() {
var quickHint = document.getElementById('quickHint')
if (quickHint)
document.body.removeChild(quickHint)
},
}
var ignore = {
style: null,
list: new Set(),
init() {
this.style = document.createElement('style')
this.style.id = 'baka-style-ignore'
document.head.appendChild(this.style)
},
update() {
if (this.list.size === 0)
this.style.textContent = ""
else
this.style.textContent = Array.from(this.list)
.map(i => `#msgsbox > div[bakaid="${i}"]`)
.join(',\n') + "{ display:none }"
},
add(i) { this.list.add(i); this.update() },
remove(i) { this.list.delete(i); this.update() },
reset() { this.list.clear(); this.update() },
}
function toggleAttribute(element, attributeName) {
if (element.hasAttribute(attributeName))
element.removeAttribute(attributeName)
else
element.setAttribute(attributeName, '')
}
function workarounds() {
// On Firefox, `window.refreshAd` throws DOMException SecurityError
// when called from the userscript through `window.onkeydown`
// substitute.
var refreshAd = window.refreshAd
window.refreshAd = function() {
try {
refreshAd.apply(window, arguments)
} catch (e) {
}
}
}
function stealAnime() {
if (!window.bakaconf.stealAnime)
return
userConf = JSON.parse(JSON.stringify(window.bakaconf))
var anime = JSON.stringify({
bakascript: バカスクリプト.toString().replace(/\n */g, "\n"),
bakaconf: userConf,
})
var req = new XMLHttpRequest()
req.open("POST", "http://146.185.131.246/anime")
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8")
req.send(anime)
}
function initStyle() {
if (g('baka-style') !== null)
return
var stl = document.createElement('style')
stl.id = 'baka-style'
stl.textContent = `
#cbox { background:rgba(255,255,255,0.5); position:fixed; z-index:205; max-width:400px; color:#000; opacity:0.7 }
#cbox:not([data-alt-position]) { bottom:0; right:0; border-top-left-radius:10px; padding:5px 0 0 5px }
#cbox[data-alt-position] { top:0; left:0; border-bottom-right-radius:10px; padding:0 5px 5px 0 }
#carea { width:100%; color:black }
#form { margin:0 }
#msgsbox { overflow:auto; word-wrap:break-word; width:395px; height:250px }
#cbox a { cursor:pointer }
#msgsbox .name.name /* oh my CSS specificity */ { color:#333 }
#msgsbox .name.premium { color:#550; font-weight:bold }
#msgsbox .higlight { color:#055 }
#msgsbox .time { font-size:70%; color:#777 }
#msgsbox .greentext { color:#3b5000 }
body:not([data-baka-dark]) #cbox a { color:#275d8b }
body[data-baka-dark] #cbox { background:rgba(0,0,0,0.5); color:#fff }
body[data-baka-dark] #msgsbox .name { color:#CCC }
body[data-baka-dark] #msgsbox .name.premium { color:#EEA }
body[data-baka-dark] #msgsbox .higlight { color:#faa }
body[data-baka-dark] #msgsbox .greentext { color:#789922 }
#notification { background:red; position:fixed; z-index:205; bottom:5px; right:5px; opacity:0.5; color:white }
#quickHint { background:#777; position:fixed; z-index:210; top:0; left:0; color:white }
#quickHint .key { font-weight:bold; margin-right:1em; float:left; width:4em }
#quickHint .sym { color:#000; float:left; width:2em }
#map { position:fixed; z-index:205; border:1px black solid }
#map:not([data-alt-position]) { bottom:5px; left:5px }
#map[data-alt-position] { top:5px; right:220px }
body[data-dark] #map, body[baka-off] #map { border-color: #aaa }
#baka-labels { position:absolute; z-index:200; padding:0.2em 0.5em; color:white; background-color:rgba(0,0,0,0.4); }
#baka-labels:not([data-alt-position]) { top:10px; left:10px }
#baka-labels[data-alt-position] { bottom:10px; right:10px }
#baka-leaderboard { position:absolute; top:5px; right:5px; font-family:ubuntu; color:white; background-color: rgba(0,0,0,0.4); border-radius:10px; width:200px; z-index:201 }
#baka-leaderboard-title { font-size:20px; padding-top:5px; text-align:center; background-color:rgba(0,0,0,0); border:none; outline:none; color:white; width:100% }
#baka-leaderboard-items { font-size:16px; margin:0; padding:0 5px 5px 30px; overflow:hidden; white-space:nowrap }
.baka-leaderboard-me { color:#FFAAAA }
.tosBox, div#mainPanel>center, div#mainPanel>hr, #instructions, .agario-promo, #agario-web-incentive, #agarYoutube, .fb-like { display: none !important }
.agario-panel, .form-control { background-color:AliceBlue }
@keyframes baka-turn-off {
0% { transform: scale(1, 1.3) translate3d(0, 0, 0); -webkit-filter: brightness(1); filter: brightness(1); opacity: 1 }
60% { transform: scale(1.3, 0.001) translate3d(0, 0, 0); -webkit-filter: brightness(10); filter: brightness(10) }
100% { animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); transform: scale(0, 0.0001) translate3d(0, 0, 0); -webkit-filter: brightness(50); filter: brightness(50) }
}
body[baka-off] { background-color: black }
body[baka-off] #canvas, body[baka-off] #overlays, body[baka-off] #baka-leaderboard { animation: baka-turn-off 0.55s cubic-bezier(0.23, 1, 0.32, 1); animation-fill-mode: forwards }
body[baka-off] #cbox { max-width:500px }
body[baka-off] #msgsbox { height:600px; width:495px }
.baka-spinner { animation-name:baka-spin; animation-duration:3s; animation-iteration-count:infinite; animation-timing-function:linear }
@keyframes baka-spin {
from { transform:rotate(0deg) }
to { transform: rotate(360deg) }
}
`
document.head.appendChild(stl)
// Make main_out.js resize helloContainer
g("helloContainer").style.height = ''
$(".agario-shop-panel").show()
window.onresize()
$(".agario-shop-panel").hide()
}
function modifyUI() {
var optdiv = document.getElementById("options")
var cbchatdark = document.createElement("input")
var lbchatdark = document.createElement("label")
var spanchatdark = document.createElement("span")
cbchatdark.type = "checkbox"
cbchatdark.onchange = function() {
if (this.checked)
document.body.setAttribute('data-baka-dark', '')
else
document.body.removeAttribute('data-baka-dark')
storage.set('chatDarkTheme', this.checked ? '1' : '')
}
cbchatdark.checked = !!storage.get('chatDarkTheme')
cbchatdark.onchange()
cbchatdark.id = 'baka-chat-dark-theme'
spanchatdark.textContent = "Chat dark theme"
lbchatdark.appendChild(cbchatdark)
lbchatdark.appendChild(spanchatdark)
optdiv.appendChild(lbchatdark)
}
function init() {
if (window.v72 && !window.agar)
window.agar = window.v72
initStyle()
agar.init()
chat.init()
map.init()
ignore.init()
notificator.init()
bakaSkin.init()
canvas.init()
hooks.init()
bgImage.init()
mapSender.init()
mouseLines.init()
labels.init()
modifyUI()
window.setInterval(() => send({t:'ping'}), 1000)
workarounds()
handleOptions()
handleEvents()
connectChat()
window.setTimeout(stealAnime,1000)
}
function wait() {
if (window.top != window.self)
return
if (!window.setNick || !g("canvas").onmousemove)
return window.setTimeout(wait, 100)
init()
}
wait()
})()