Sleazy Fork is available in English.
:joy:
// ==UserScript==
// @name auto shield ... let's go
// @namespace aa
// @version 39.0.0
// @description :joy:
// @license MI
// @match *://agmaclone.xyz/*
// @match *://*.agmaclone.xyz/*
// @run-at document-start
// @grant unsafeWindow
// ==/UserScript==
(() => {
'use strict';
const PAGE = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
const KEY = '__AGMACLONE_NATIVE_DEFENSE_V38__';
if (PAGE[KEY]) return;
const C = {
autoShield: true,
autoAntiFreeze: true,
shieldDelayMs: 0,
antiFreezeDelayMs: 0,
shieldRetryMs: 20,
shieldRetryMax: Infinity,
shieldRetryCheckMs: 20,
shieldRetryMinSendGapMs: 100,
shieldRetryCooldownSafetyMs: 12,
antiFreezeRetryMs: 90,
antiFreezeRetryMax: 1,
antiFreezePacketDirectEnabled: true,
antiFreezeFastCacheMaxAgeMs: 220,
statePollMs: 0,
wsSyncMinMs: 0,
wsNodeSyncDelayMs: 0,
uiUpdateMinMs: 140,
selfDropMaxCellAgeMs: 1800,
selfDropFailClosed: true,
shieldWatchdogEnabled: true,
shieldWatchdogGraceMs: 45,
shieldWatchdogRetryGapMs: 900,
shieldPendingStaleMs: 650,
shieldFastCacheMaxAgeMs: 220,
shieldRetryScheduleMs: [],
shieldTimingHistoryMax: 30,
shieldHotkeyCode: '',
shieldHotkeyStopGameKey: true,
fastShieldHotkeyCode: '',
fastShieldHotkeyStopGameKey: true,
fastShieldIntervalMs: 50,
pushBurstEnabled: true,
pushBurstHotkeyCode: 'Digit3',
pushBurstCount: 15,
pushBurstIntervalMs: 10.5,
pushBurstStopGameKey: true,
growthHotkeyCode: '',
growthHotkeyStopGameKey: true,
growthAmount: 30,
autoPelletEnabled: false,
autoPelletAmount: 30,
autoPelletIntervalMs: 5000,
autoPelletWorldX: null,
autoPelletWorldY: null,
autoPelletSetMouseHotkeyCode: '',
autoPelletSetMouseHotkeyStopGameKey: true,
comboHotkeyCode: '',
comboHotkeyStopGameKey: true,
comboAtomicFirstStage: false,
comboSecondPortalExtraMs: 25,
comboTargetMaxCellAgeMs: 1800,
comboSecondPortalMinDelayMs: 12,
comboQueueMax: 3,
comboSecondPortalWsRetryMs: 100,
comboSecondPortalWsRetryMax: 10,
comboPortalConfirmPollMs: 12,
comboPortalConfirmTimeoutMs: 900,
comboPortalRetryMax: 2,
comboPortalRetryExtraMs: 90,
comboSecondPortalOffsetEnabled: true,
comboSecondPortalOffsetMin: 60,
comboSecondPortalOffsetSizeFactor: 0.45,
comboSecondPortalOffsetMax: 240,
manualMouseFreshMs: 30000,
autoViewAntiRec: true,
autoViewFreeze: true,
viewTargetMode: 'mouse-hover-center',
viewCenterDropOnly: true,
viewScanMs: 0,
viewRange: 6800,
viewMaxCellAgeMs: 1800,
viewMinEnemySize: 1,
viewMouseTargetExtraRadius: 80,
viewFreezeCloseExtra: 900,
viewFreezeCloseMinDist: 1700,
viewAimDotMin: 0.0,
viewAimClosingMin: 0.01,
viewAimMinSpeed: 0.01,
viewAimLineExtra: 2400,
viewAimLineForwardMin: 0,
viewPredictGain: 0,
viewPredictMs: 1300,
viewAttackPredictMs: 850,
viewAttackCloseMargin: 900,
viewAttackPredictMargin: 1450,
viewRushHitMargin: 700,
viewRushMinSpeed: 0.01,
viewRushMinClosing: 0.01,
viewEmergencyMaxDist: 6200,
viewEmergencySpeed: 0.01,
viewEmergencyClosing: 0.01,
viewEmergencyAimDot: 0.01,
viewForwardDropEnabled: false,
viewForwardDropBase: 120,
viewForwardDropSizeFactor: 0.75,
viewForwardDropSpeedFactor: 0.015,
viewForwardDropMin: 140,
viewForwardDropMax: 850,
viewAntiRecFallbackLockMs: 460,
viewFreezeFallbackLockMs: 2900,
viewAntiRecPerPlayerLockMs: 460,
viewFreezePerPlayerLockMs: 2900,
antiRecContinuousPerPlayer: true,
antiRecLoopIdleMs: 30,
antiRecLoopFailureRetryMs: 40,
antiRecLoopMinTimerMs: 1,
autoRecEnabled: false,
autoRecIntervalMs: 1,
shieldBreakEnabled: false,
shieldBreakPlayerId: 0,
shieldBreakRetryMs: 35,
targetAutoFreezeEnabled: false,
targetAutoAntiRecEnabled: false,
targetAntiRecIntervalMs: 460,
targetActionRetryMs: 35,
panel: true,
panelMinimized: false,
debug: false,
storageKey: '__AGMACLONE_NATIVE_DEFENSE_V32_PREFS__'
};
const S = {
ws: null,
nativeSend: null,
seenSockets: new WeakSet(),
PacketIds: null,
EntityTypes: null,
PlayerFlags: null,
mapsReady: false,
mapsError: '',
mainPlayerId: 0,
lastAnimations: null,
lastOwnCount: 0,
fastOwnCell: null,
fastOwnCellAt: 0,
lastShieldClearPacketAt: 0,
lastShieldReactionMs: null,
lastShieldReactionUs: null,
shieldTiming: {
lastOnAtPerf: 0,
lastOffAtPerf: 0,
lastSendAtPerf: 0,
lastOnAtWall: 0,
lastOffAtWall: 0,
lastSendAtWall: 0,
lastClearToSendMs: null,
lastSendToOnMs: null,
lastClearToOnMs: null,
lastVisibleDurationMs: null,
lastUnshieldedGapMs: null,
lastSendIntervalMs: null,
lastClearIntervalMs: null,
lastOnIntervalMs: null,
sendsThisCycle: 0,
cyclesCompleted: 0,
lastSendReason: '',
pendingCycle: null,
history: []
},
lastVerifiedSelfCell: null,
selfDropBlocks: 0,
shieldMissingSince: 0,
shieldLastWatchdogKickAt: 0,
shieldWatchdogRecoveries: 0,
shieldPendingCreatedAt: 0,
lastShieldWatchdogReason: '',
shieldCycle: 0,
shieldAttempts: 0,
shieldPending: null,
lastShieldSendAt: 0,
freezeCycle: 0,
antiFreezeAttempts: 0,
antiFreezePending: null,
lastAntiFreezeSendAt: 0,
lastAntiFreezeFrozenPacketAt: 0,
lastAntiFreezeReactionMs: null,
lastAntiFreezeReactionUs: null,
antiFreezePacketDirectSends: 0,
antiFreezePacketDirectFallbacks: 0,
pushBurstRunning: false,
pushBurstTimer: null,
pushBurstToken: 0,
pushBursts: 0,
pushPacketsSent: 0,
lastPushBurst: null,
fastShieldRunning: false,
fastShieldTimer: null,
fastShieldAttempts: 0,
fastShieldSends: 0,
lastFastShield: null,
hotkeyCapture: null,
hotkeyCaptureStartedAt: 0,
hotkeyMessage: '',
mouseWorld: null,
lastNativeMousePacket: null,
renderTransform: {
scale: 0,
nodeX: 0,
nodeY: 0,
canvasWidth: 0,
canvasHeight: 0,
updatedAt: 0
},
outgoingPacketsObserved: 0,
manualItemLastSend: Object.create(null),
growthSends: 0,
lastGrowthSend: null,
autoPelletTimer: null,
autoPelletRuns: 0,
autoPelletPackets: 0,
autoPelletTotal: 0,
lastAutoPellet: null,
comboRunning: false,
comboToken: 0,
comboSecondPortalTimer: null,
comboQueue: [],
comboSends: 0,
comboBlocks: 0,
lastComboSend: null,
lastComboBlock: null,
powerCooldowns: Object.create(null),
inventoryCounts: Object.create(null),
inventoryPreviousCounts: Object.create(null),
inventoryItemUpdatedAt: Object.create(null),
inventoryItemUpdateSeq: Object.create(null),
inventoryUpdateSeq: 0,
unlimitedInventory: false,
inventoryKnown: false,
viewMotion: new Map(),
viewGlobalLocks: new Map(),
viewTargetLocks: new Map(),
lastViewScanAt: 0,
viewScans: 0,
viewDrops: 0,
viewBlocks: 0,
lastViewTarget: null,
lastViewDrop: null,
lastViewBlock: null,
viewRoundRobin: {
antiRec: 0,
freeze: 0
},
visibleEnemyOwnerIds: [],
visibleEnemyCount: 0,
visibleEnemyUpdatedAt: 0,
antiRecLoopTimer: null,
antiRecLoopDueAt: 0,
antiRecLoopRuns: 0,
antiRecLoopDrops: 0,
antiRecLoopLastRunAt: 0,
antiRecLoopLastSource: '',
antiRecLoopLastVisibleCount: 0,
antiRecLoopLastSentOwners: [],
autoRecTimer: null,
autoRecDueAt: 0,
autoRecRuns: 0,
autoRecSends: 0,
autoRecLastSendAt: 0,
autoRecLastSource: '',
shieldBreakTargetId: 0,
shieldBreakLastAnimations: null,
shieldBreakShieldOn: false,
shieldBreakPortalSent: false,
shieldBreakTimer: null,
shieldBreakAttempts: 0,
shieldBreakSends: 0,
shieldBreakDetectedAt: 0,
shieldBreakLastSend: null,
shieldBreakLastBlock: null,
shieldBreakPlayerListSignature: '',
targetFreezeTimer: null,
targetFreezeSends: 0,
targetFreezeAttempts: 0,
targetFreezeLastSend: null,
targetFreezeLastBlock: null,
targetAntiRecTimer: null,
targetAntiRecSends: 0,
targetAntiRecAttempts: 0,
targetAntiRecLastSend: null,
targetAntiRecLastBlock: null,
lastDrop: null,
lastBlock: null,
lastPacketOp: -1,
messages: 0,
socketsSeen: 0,
early: {
installed: false,
captures: 0,
errors: []
},
panel: null,
poll: null,
syncTimer: null,
syncLastAt: 0,
syncPendingSource: '',
syncRuns: 0,
syncRequests: 0,
uiUpdateTimer: null,
uiLastUpdateAt: 0,
uiUpdates: 0,
uiRequests: 0
};
function log(...args) {
if (C.debug) console.log('[AC Shield Until Effect v37]', ...args);
}
function now() {
return Date.now();
}
function loadPrefs() {
try {
const raw =
localStorage.getItem(C.storageKey) ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V31_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V30_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V29_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V28_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V27_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V26_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V25_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V24_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V23_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V22_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V21_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V20_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V19_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V18_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V17_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V16_PREFS__') ||
localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V10_PREFS__') ||
'{}';
const p = JSON.parse(raw);
if (typeof p.autoShield === 'boolean') C.autoShield = p.autoShield;
if (typeof p.autoAntiFreeze === 'boolean') C.autoAntiFreeze = p.autoAntiFreeze;
if (typeof p.autoViewAntiRec === 'boolean') C.autoViewAntiRec = p.autoViewAntiRec;
if (typeof p.autoViewFreeze === 'boolean') C.autoViewFreeze = p.autoViewFreeze;
if (typeof p.autoRecEnabled === 'boolean') C.autoRecEnabled = p.autoRecEnabled;
if (typeof p.shieldBreakEnabled === 'boolean') C.shieldBreakEnabled = p.shieldBreakEnabled;
if (typeof p.targetAutoFreezeEnabled === 'boolean') C.targetAutoFreezeEnabled = p.targetAutoFreezeEnabled;
if (typeof p.targetAutoAntiRecEnabled === 'boolean') C.targetAutoAntiRecEnabled = p.targetAutoAntiRecEnabled;
if (Number.isFinite(Number(p.shieldBreakPlayerId))) {
const id = Math.trunc(Number(p.shieldBreakPlayerId));
C.shieldBreakPlayerId = id >= 1 && id <= 65535 ? id : 0;
}
if (typeof p.panelMinimized === 'boolean') C.panelMinimized = p.panelMinimized;
if (typeof p.autoPelletEnabled === 'boolean') C.autoPelletEnabled = p.autoPelletEnabled;
if (p.autoPelletWorldX !== null && p.autoPelletWorldX !== '' && Number.isFinite(Number(p.autoPelletWorldX))) C.autoPelletWorldX = Number(p.autoPelletWorldX);
if (p.autoPelletWorldY !== null && p.autoPelletWorldY !== '' && Number.isFinite(Number(p.autoPelletWorldY))) C.autoPelletWorldY = Number(p.autoPelletWorldY);
if (typeof p.autoPelletSetMouseHotkeyCode === 'string') C.autoPelletSetMouseHotkeyCode = p.autoPelletSetMouseHotkeyCode;
if (typeof p.shieldHotkeyCode === 'string') C.shieldHotkeyCode = p.shieldHotkeyCode;
if (typeof p.fastShieldHotkeyCode === 'string') C.fastShieldHotkeyCode = p.fastShieldHotkeyCode;
if (typeof p.pushBurstHotkeyCode === 'string' && p.pushBurstHotkeyCode) {
C.pushBurstHotkeyCode = p.pushBurstHotkeyCode;
}
if (typeof p.growthHotkeyCode === 'string') {
C.growthHotkeyCode = p.growthHotkeyCode;
}
if (typeof p.comboHotkeyCode === 'string') {
C.comboHotkeyCode = p.comboHotkeyCode;
}
} catch (_) {}
}
function savePrefs() {
try {
localStorage.setItem(C.storageKey, JSON.stringify({
autoShield: !!C.autoShield,
autoAntiFreeze: !!C.autoAntiFreeze,
autoViewAntiRec: !!C.autoViewAntiRec,
autoViewFreeze: !!C.autoViewFreeze,
autoRecEnabled: !!C.autoRecEnabled,
shieldBreakEnabled: !!C.shieldBreakEnabled,
targetAutoFreezeEnabled: !!C.targetAutoFreezeEnabled,
targetAutoAntiRecEnabled: !!C.targetAutoAntiRecEnabled,
shieldBreakPlayerId: Number(C.shieldBreakPlayerId) || 0,
panelMinimized: !!C.panelMinimized,
autoPelletEnabled: !!C.autoPelletEnabled,
autoPelletWorldX: Number.isFinite(Number(C.autoPelletWorldX)) ? Number(C.autoPelletWorldX) : null,
autoPelletWorldY: Number.isFinite(Number(C.autoPelletWorldY)) ? Number(C.autoPelletWorldY) : null,
autoPelletSetMouseHotkeyCode: String(C.autoPelletSetMouseHotkeyCode || ''),
shieldHotkeyCode: String(C.shieldHotkeyCode || ''),
fastShieldHotkeyCode: String(C.fastShieldHotkeyCode || ''),
pushBurstHotkeyCode: String(C.pushBurstHotkeyCode || 'Digit3'),
growthHotkeyCode: String(C.growthHotkeyCode || ''),
comboHotkeyCode: String(C.comboHotkeyCode || '')
}));
} catch (_) {}
}
loadPrefs();
async function fetchJson(path) {
const fn = PAGE.fetch || fetch;
const response = await Reflect.apply(fn, PAGE, [path, { cache: 'no-store' }]);
if (!response.ok) throw new Error(`${path}: HTTP ${response.status}`);
return await response.json();
}
async function loadNativeMaps() {
try {
const [packetIds, entityTypes, playerFlags] = await Promise.all([
fetchJson('/share/PacketIds.json'),
fetchJson('/share/EntityTypes.json'),
fetchJson('/share/PlayerFlags.json')
]);
S.PacketIds = packetIds;
S.EntityTypes = entityTypes;
S.PlayerFlags = playerFlags;
S.mapsReady = true;
S.mapsError = '';
log('native maps loaded', { packetIds, entityTypes, playerFlags });
updatePanel();
return true;
} catch (e) {
S.mapsReady = false;
S.mapsError = e && e.message ? e.message : String(e);
updatePanel();
return false;
}
}
function packetBuffer(data) {
if (data instanceof ArrayBuffer) return data;
if (ArrayBuffer.isView(data)) {
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
}
if (data && data.data instanceof ArrayBuffer) return data.data;
return null;
}
function readUtf8Zero(dv, state) {
const bytes = [];
while (state.o < dv.byteLength) {
const b = dv.getUint8(state.o++);
if (b === 0) break;
bytes.push(b);
}
try {
return new TextDecoder('utf-8').decode(new Uint8Array(bytes));
} catch (_) {
return '';
}
}
function parseHeader(dv) {
let offset = 0;
if (dv.byteLength >= 6 && dv.getUint8(0) === 240) offset = 5;
if (offset >= dv.byteLength) return null;
return { op: dv.getUint8(offset++), offset };
}
function parseInitPlayer(dv, offset) {
if (offset + 2 > dv.byteLength) return;
const nextId = dv.getUint16(offset, true);
if (Number(nextId) !== Number(S.mainPlayerId)) {
S.mainPlayerId = nextId;
resetDefenseState('init-player-changed');
} else {
S.mainPlayerId = nextId;
}
log('main player id', S.mainPlayerId);
}
function parseInitServerCooldowns(dv, offset) {
const E = S.EntityTypes;
if (!E || offset >= dv.byteLength) return;
// New protocol: serverConfig byte, then 13 cooldown bytes.
// MOTHERCELL was inserted before PORTAL in the updated client.
const serverConfig = dv.getUint8(offset++);
S.unlimitedInventory = !!(serverConfig & 1);
const order = [
E.RECOMBINE,
E.VIRUS,
E.MOTHERCELL,
E.PORTAL,
E.SPEED,
E.ANTIRECOMBINE,
E.ANTIFREEZE,
E.SHIELD,
E.FREEZE,
E.GOLDBLOCK,
E.PUSH,
E.GROWTH,
E.FROZENVIRUS
];
for (const item of order) {
if (offset >= dv.byteLength) break;
const raw = dv.getUint8(offset++);
if (Number.isFinite(Number(item))) {
S.powerCooldowns[Number(item)] = raw * 10.5;
}
}
}
function parseUpdateInventory(dv, offset) {
while (offset + 5 <= dv.byteLength) {
const item = Number(dv.getUint8(offset++));
const count = Number(dv.getUint32(offset, true));
offset += 4;
const previous = Number(S.inventoryCounts[item]);
if (Number.isFinite(previous)) {
S.inventoryPreviousCounts[item] = previous;
}
S.inventoryCounts[item] = count;
S.inventoryUpdateSeq++;
S.inventoryItemUpdateSeq[item] = S.inventoryUpdateSeq;
S.inventoryItemUpdatedAt[item] = now();
S.inventoryKnown = true;
}
}
function parseUpdatePlayers(dv, offset, receivedAtPerf = performance.now()) {
const F = S.PlayerFlags;
if (!F) return;
const st = { o: offset };
while (st.o + 3 <= dv.byteLength) {
const id = dv.getUint16(st.o, true);
st.o += 2;
const flags = dv.getUint8(st.o++);
if (flags & F.NICKNAME_CHANGED) readUtf8Zero(dv, st);
if (flags & F.COLOR_CHANGED) st.o += 3;
if (flags & F.NICKNAME_COLOR_CHANGED) st.o += 3;
if (flags & F.NICKNAME_STROKE_COLOR_CHANGED) st.o += 3;
if (flags & F.SKIN_CHANGED) readUtf8Zero(dv, st);
if (flags & F.BADGES_CHANGED) {
if (st.o >= dv.byteLength) return;
const count = dv.getUint8(st.o++);
st.o += count;
}
if (flags & F.USERNAME_CHANGED) readUtf8Zero(dv, st);
if (flags & F.ANIMATIONS_CHANGED) {
if (st.o >= dv.byteLength) return;
const animations = dv.getUint8(st.o++);
if (animations & F.ANIMATION_SHIELD) st.o += 1;
if (animations & F.ANIMATION_PUSH) st.o += 4;
if (id === S.mainPlayerId) {
processAnimationState(animations, 'packet-update-players', {
packetDirect: true,
packetAt: receivedAtPerf
});
}
if (Number(id) === Number(C.shieldBreakPlayerId)) {
processShieldBreakAnimation(id, animations, 'packet-update-players');
processTargetIdAnimation(id, animations, 'packet-update-players');
}
}
if (st.o > dv.byteLength) return;
}
}
function onGameMessage(event, ws) {
const receivedAtPerf = performance.now();
const buffer = packetBuffer(event && event.data);
if (!buffer || buffer.byteLength < 1) return;
S.messages++;
try {
const dv = new DataView(buffer);
const h = parseHeader(dv);
if (!h) return;
S.lastPacketOp = h.op;
if (!S.mapsReady || !S.PacketIds) {
// The maps normally load before the server begins sending gameplay packets.
return;
}
let syncSource = '';
let syncDelayMs = 0;
if (h.op === S.PacketIds.INIT_PLAYER) {
parseInitPlayer(dv, h.offset);
syncSource = 'ws-init-player';
} else if (h.op === S.PacketIds.INIT_SERVER) {
parseInitServerCooldowns(dv, h.offset);
syncSource = 'ws-init-server';
} else if (h.op === S.PacketIds.UPDATE_PLAYERS) {
// Shield and Anti-Freeze are already handled synchronously above.
// The coalesced sync only refreshes caches/watchdog/View afterward.
parseUpdatePlayers(dv, h.offset, receivedAtPerf);
syncSource = 'ws-update-players';
} else if (
Number.isFinite(Number(S.PacketIds.UPDATE_INVENTORY)) &&
h.op === Number(S.PacketIds.UPDATE_INVENTORY)
) {
parseUpdateInventory(dv, h.offset);
updatePanel();
} else if (
Number.isFinite(Number(S.PacketIds.UPDATE_NODES)) &&
h.op === Number(S.PacketIds.UPDATE_NODES)
) {
// UPDATE_NODES is frequent. Never perform a full scan for every packet.
syncSource = 'ws-update-nodes';
{
const configuredDelay = Number(C.wsNodeSyncDelayMs);
syncDelayMs = Math.max(
0,
Number.isFinite(configuredDelay) ? configuredDelay : 0
);
}
} else if (
Number.isFinite(Number(S.PacketIds.REMOVED_NODES)) &&
h.op === Number(S.PacketIds.REMOVED_NODES)
) {
syncSource = 'ws-removed-nodes';
{
const configuredDelay = Number(C.wsNodeSyncDelayMs);
syncDelayMs = Math.max(
0,
Number.isFinite(configuredDelay) ? configuredDelay : 0
);
}
}
if (syncSource) {
// Event listeners finish before the microtask, allowing main_out.js to
// apply PAGE.players/PAGE.cells first.
queueMicrotask(() => scheduleClientSync(syncSource, syncDelayMs));
}
} catch (e) {
S.lastBlock = {
reason: 'message-parse-error',
detail: e && e.message ? e.message : String(e),
t: now()
};
}
}
function updateWorldMouseFromScreen(screenX, screenY, source = 'screen-mouse') {
const tr = S.renderTransform;
const scale = Number(tr && tr.scale);
const width = Number(tr && tr.canvasWidth) || innerWidth;
const height = Number(tr && tr.canvasHeight) || innerHeight;
const nodeX = Number(tr && tr.nodeX);
const nodeY = Number(tr && tr.nodeY);
if (
!Number.isFinite(scale) || scale <= 0 ||
!Number.isFinite(nodeX) || !Number.isFinite(nodeY)
) return false;
const x = (Number(screenX) - width / 2) / scale + nodeX;
const y = (Number(screenY) - height / 2) / scale + nodeY;
if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
S.mouseWorld = { x: x | 0, y: y | 0, source, t: now() };
return true;
}
function installRenderTransformCapture() {
try {
const proto = PAGE.CanvasRenderingContext2D && PAGE.CanvasRenderingContext2D.prototype;
if (!proto || proto.__AC_RENDER_CAPTURE_V38__) return true;
const nativeScale = proto.scale;
const nativeTranslate = proto.translate;
const state = new WeakMap();
proto.scale = function(x, y) {
try {
if (this && this.canvas && this.canvas.id === 'canvas') {
const rec = state.get(this) || {};
rec.scale = Number(x);
rec.sawScale = true;
rec.canvasWidth = Number(this.canvas.width) || innerWidth;
rec.canvasHeight = Number(this.canvas.height) || innerHeight;
state.set(this, rec);
}
} catch (_) {}
return Reflect.apply(nativeScale, this, [x, y]);
};
proto.translate = function(x, y) {
try {
if (this && this.canvas && this.canvas.id === 'canvas') {
const rec = state.get(this) || {};
if (rec.sawScale && Number(x) < 0 && Number(y) < 0) {
S.renderTransform = {
scale: Number(rec.scale) || 0,
nodeX: -Number(x),
nodeY: -Number(y),
canvasWidth: Number(this.canvas.width) || innerWidth,
canvasHeight: Number(this.canvas.height) || innerHeight,
updatedAt: now()
};
rec.sawScale = false;
}
state.set(this, rec);
}
} catch (_) {}
return Reflect.apply(nativeTranslate, this, [x, y]);
};
Object.defineProperty(proto, '__AC_RENDER_CAPTURE_V38__', { value: true });
PAGE.addEventListener('mousemove', event => {
updateWorldMouseFromScreen(event.clientX, event.clientY, 'dom-mouse-render-transform');
}, true);
return true;
} catch (e) {
S.lastBlock = { reason: 'render-transform-hook-failed', detail: String(e), t: now() };
return false;
}
}
function observeOutgoingPacket(data) {
const buffer = packetBuffer(data);
if (!buffer || buffer.byteLength < 1 || !S.PacketIds) return;
try {
const dv = new DataView(buffer);
const op = dv.getUint8(0);
const P = S.PacketIds;
const E = S.EntityTypes || {};
S.outgoingPacketsObserved++;
// New client MOUSE packet: [opcode, normalizedScreenX, normalizedScreenY].
// Actual world coordinates are reconstructed from the latest render transform
// captured from the game canvas.
if (
Number.isFinite(Number(P.MOUSE)) &&
op === Number(P.MOUSE) &&
dv.byteLength >= 3
) {
const bx = dv.getUint8(1);
const by = dv.getUint8(2);
S.lastNativeMousePacket = new Uint8Array([Number(P.MOUSE) & 255, bx, by]);
const sx = bx / 255 * innerWidth;
const sy = by / 255 * innerHeight;
updateWorldMouseFromScreen(sx, sy, 'native-outgoing-mouse-v2');
return;
}
// New drop packets contain only their own opcode (+ optional Growth amount).
const pairs = [
[E.RECOMBINE, P.DROP_RECOMBINE],
[E.SPEED, P.DROP_SPEED],
[E.VIRUS, P.DROP_VIRUS],
[E.MOTHERCELL, P.DROP_MOTHERCELL],
[E.PORTAL, P.DROP_PORTAL],
[E.SHIELD, P.DROP_SHIELD],
[E.FREEZE, P.DROP_FREEZE],
[E.GROWTH, P.DROP_GROWTH],
[E.ANTIRECOMBINE, P.DROP_ANTIRECOMBINE],
[E.ANTIFREEZE, P.DROP_ANTIFREEZE],
[E.FROZENVIRUS, P.DROP_FROZENVIRUS],
[E.GOLDBLOCK, P.DROP_GOLDBLOCK]
];
for (const [item, packetId] of pairs) {
if (!Number.isFinite(Number(item)) || !Number.isFinite(Number(packetId))) continue;
if (op !== Number(packetId)) continue;
S.manualItemLastSend[Number(item)] = now();
return;
}
} catch (_) {}
}
function attachSocket(ws, source = 'unknown') {
if (!ws || S.seenSockets.has(ws)) return ws;
S.seenSockets.add(ws);
S.socketsSeen++;
S.ws = ws;
try { ws.binaryType = 'arraybuffer'; } catch (_) {}
ws.addEventListener('open', () => {
S.ws = ws;
resetDefenseState('ws-open');
updatePanel();
});
ws.addEventListener('message', event => onGameMessage(event, ws));
ws.addEventListener('close', () => {
if (S.ws === ws) S.ws = null;
resetDefenseState('ws-close');
updatePanel();
});
if (ws.readyState === 1) S.ws = ws;
log('socket attached', source, ws.url);
updatePanel();
return ws;
}
function installEarlySocketCapture() {
try {
const WS = PAGE.WebSocket;
const proto = WS && WS.prototype;
if (!WS || !proto) throw new Error('WebSocket unavailable');
S.nativeSend = proto.send;
// Observe the original client's outgoing packets. Script-originated
// packets use S.nativeSend directly and are recorded explicitly below.
if (!proto.send.__AC_TARGET_OBSERVER_V18__) {
const originalSend = S.nativeSend;
const wrappedSend = function(data) {
observeOutgoingPacket(data);
return Reflect.apply(originalSend, this, [data]);
};
Object.defineProperty(wrappedSend, '__AC_TARGET_OBSERVER_V18__', {
value: true
});
proto.send = wrappedSend;
}
const capture = (ws, source) => {
S.early.captures++;
attachSocket(ws, source);
};
for (const prop of ['onopen', 'onmessage', 'onclose', 'onerror']) {
try {
const descriptor = Object.getOwnPropertyDescriptor(proto, prop);
if (!descriptor || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') {
S.early.errors.push(`${prop}: descriptor missing`);
continue;
}
const get = descriptor.get;
const set = descriptor.set;
Object.defineProperty(proto, prop, {
configurable: true,
enumerable: descriptor.enumerable,
get() {
return Reflect.apply(get, this, []);
},
set(value) {
capture(this, `property-${prop}`);
return Reflect.apply(set, this, [value]);
}
});
} catch (e) {
S.early.errors.push(`${prop}: ${e && e.message ? e.message : String(e)}`);
}
}
// Constructor Proxy is a fallback. Property capture remains the main path.
if (!WS.__AC_NATIVE_DEFENSE_PROXY__) {
const Wrapped = new Proxy(WS, {
construct(Target, args, NewTarget) {
const socket = Reflect.construct(Target, args, NewTarget === Wrapped ? Target : NewTarget);
return attachSocket(socket, 'constructor-proxy');
}
});
Object.setPrototypeOf(Wrapped, WS);
Wrapped.prototype = WS.prototype;
Object.defineProperty(Wrapped, '__AC_NATIVE_DEFENSE_PROXY__', { value: true });
PAGE.WebSocket = Wrapped;
}
S.early.installed = true;
return true;
} catch (e) {
S.early.errors.push(e && e.message ? e.message : String(e));
return false;
}
}
function pagePlayers() {
return PAGE.players && typeof PAGE.players === 'object' ? PAGE.players : null;
}
function pageCells() {
return PAGE.cells && typeof PAGE.cells === 'object' ? PAGE.cells : null;
}
function ownCells() {
const cells = pageCells();
const E = S.EntityTypes;
if (!cells || !E || !S.mainPlayerId) return [];
const out = [];
for (const key of Object.keys(cells)) {
const c = cells[key];
if (!c || c.destroyed) continue;
if (Number(c.owner) !== Number(S.mainPlayerId)) continue;
if (Number(c.type) !== Number(E.PLAYER)) continue;
// nx/ny are the newest server-target coordinates. x/y are visually
// interpolated and can lag behind while the cell is moving quickly.
const x = Number.isFinite(c.nx) ? c.nx : c.x;
const y = Number.isFinite(c.ny) ? c.ny : c.y;
const size = Number.isFinite(c.nSize)
? c.nSize
: Number.isFinite(c.originalSize)
? c.originalSize
: c.size;
if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
out.push({
raw: c,
id: Number(c.id) || Number(key) || 0,
x,
y,
size: Number(size) || 0
});
}
out.sort((a, b) => b.size - a.size);
if (out[0]) {
S.fastOwnCell = {
raw: out[0].raw,
id: out[0].id,
owner: Number(out[0].raw && out[0].raw.owner) || Number(S.mainPlayerId),
x: out[0].x,
y: out[0].y,
size: out[0].size,
source: 'cached-live-own-nx-ny'
};
S.fastOwnCellAt = now();
}
return out;
}
function strictLiveOwnDropCell() {
const player = mainPlayer();
const E = S.EntityTypes;
const playerType = E && Number(E.PLAYER);
if (
!player ||
!player.cells ||
typeof player.cells[Symbol.iterator] !== 'function' ||
!S.mainPlayerId ||
!Number.isFinite(playerType)
) {
return null;
}
const t = now();
const candidates = [];
for (const c of player.cells) {
if (!c || c.destroyed) continue;
if (Number(c.owner) !== Number(S.mainPlayerId)) continue;
if (Number(c.type) !== playerType) continue;
// Use the latest server coordinates, never mouse coordinates and never a
// visually interpolated position unless nx/ny are unavailable.
const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
const size = Number.isFinite(c.nSize)
? Number(c.nSize)
: Number.isFinite(c.originalSize)
? Number(c.originalSize)
: Number(c.size);
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(size) || size <= 0) {
continue;
}
const updatedAt = Number(c.updateTime || c.time || 0);
const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;
if (
updatedAt > 0 &&
C.selfDropMaxCellAgeMs > 0 &&
ageMs > C.selfDropMaxCellAgeMs
) {
continue;
}
candidates.push({
raw: c,
id: Number(c.id) || 0,
owner: Number(c.owner) || 0,
x,
y,
size,
ageMs,
source: Number.isFinite(c.nx) && Number.isFinite(c.ny)
? 'live-own-nx-ny'
: 'live-own-x-y-fallback'
});
}
if (!candidates.length) return null;
// Largest cell is the safest target because its center has the widest
// tolerance and cannot accidentally land outside the player's body.
candidates.sort((a, b) => (b.size - a.size) || (a.ageMs - b.ageMs));
const best = candidates[0];
S.lastVerifiedSelfCell = {
id: best.id,
owner: best.owner,
x: best.x | 0,
y: best.y | 0,
size: Math.round(best.size),
ageMs: Math.round(best.ageMs),
source: best.source,
t
};
S.fastOwnCell = {
raw: best.raw,
id: best.id,
owner: best.owner,
x: best.x,
y: best.y,
size: best.size,
ageMs: best.ageMs,
source: best.source
};
S.fastOwnCellAt = t;
return best;
}
function fastOwnCell(maxAgeMs = C.shieldFastCacheMaxAgeMs) {
const cached = S.fastOwnCell;
const raw = cached && cached.raw;
const E = S.EntityTypes;
const t = now();
// O(1) hot path: validate the cached raw cell and read its newest nx/ny.
if (
cached &&
raw &&
!raw.destroyed &&
Number(raw.owner) === Number(S.mainPlayerId) &&
E &&
Number(raw.type) === Number(E.PLAYER) &&
(!S.fastOwnCellAt || t - S.fastOwnCellAt <= Math.max(0, Number(maxAgeMs) || 0))
) {
const x = Number.isFinite(Number(raw.nx)) ? Number(raw.nx) : Number(raw.x);
const y = Number.isFinite(Number(raw.ny)) ? Number(raw.ny) : Number(raw.y);
const size = Number.isFinite(Number(raw.nSize))
? Number(raw.nSize)
: Number.isFinite(Number(raw.originalSize))
? Number(raw.originalSize)
: Number(raw.size);
const updatedAt = Number(raw.updateTime || raw.time || 0);
const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;
if (
Number.isFinite(x) &&
Number.isFinite(y) &&
Number.isFinite(size) &&
size > 0 &&
(
!updatedAt ||
!C.selfDropMaxCellAgeMs ||
ageMs <= C.selfDropMaxCellAgeMs
)
) {
const verified = {
raw,
id: Number(raw.id) || Number(cached.id) || 0,
owner: Number(raw.owner) || 0,
x,
y,
size,
ageMs,
source: 'fast-cache-live-own-nx-ny'
};
S.lastVerifiedSelfCell = {
id: verified.id,
owner: verified.owner,
x: verified.x | 0,
y: verified.y | 0,
size: Math.round(verified.size),
ageMs: Math.round(verified.ageMs),
source: verified.source,
t
};
return verified;
}
}
// Safe fallback if cache is absent/stale.
return strictLiveOwnDropCell();
}
function mainPlayer() {
const players = pagePlayers();
return players && S.mainPlayerId ? players[S.mainPlayerId] : null;
}
function liveAnimations() {
const player = mainPlayer();
return player && Number.isFinite(Number(player.animations))
? Number(player.animations)
: null;
}
function flagsFromAnimations(animations) {
const F = S.PlayerFlags || {};
animations = Number(animations) || 0;
return {
animations,
shield: !!(animations & Number(F.ANIMATION_SHIELD || 0)),
frozen: !!(animations & Number(F.ANIMATION_FROZEN || 0)),
antiFreeze: !!(animations & Number(F.ANIMATION_ANTI_FREEZE || 0))
};
}
function cancelTimer(name) {
if (S[name]) {
clearTimeout(S[name]);
S[name] = null;
}
if (name === 'shieldPending') S.shieldPendingCreatedAt = 0;
}
function dropPacketIdForItem(item) {
const P = S.PacketIds || {};
const E = S.EntityTypes || {};
item = Number(item);
const map = new Map([
[Number(E.RECOMBINE), Number(P.DROP_RECOMBINE)],
[Number(E.SPEED), Number(P.DROP_SPEED)],
[Number(E.VIRUS), Number(P.DROP_VIRUS)],
[Number(E.MOTHERCELL), Number(P.DROP_MOTHERCELL)],
[Number(E.PORTAL), Number(P.DROP_PORTAL)],
[Number(E.SHIELD), Number(P.DROP_SHIELD)],
[Number(E.FREEZE), Number(P.DROP_FREEZE)],
[Number(E.GROWTH), Number(P.DROP_GROWTH)],
[Number(E.ANTIRECOMBINE), Number(P.DROP_ANTIRECOMBINE)],
[Number(E.ANTIFREEZE), Number(P.DROP_ANTIFREEZE)],
[Number(E.FROZENVIRUS), Number(P.DROP_FROZENVIRUS)],
[Number(E.GOLDBLOCK), Number(P.DROP_GOLDBLOCK)]
]);
const id = map.get(item);
return Number.isFinite(id) ? id & 255 : null;
}
function buildDropPacket(item, x, y, amount = null) {
// Updated protocol: every droppable power has its own opcode.
// The server uses the latest MOUSE packet for position, so X/Y are NOT
// included in the drop packet anymore. Growth optionally carries amount.
const id = dropPacketIdForItem(item);
if (id == null) throw new Error(`drop opcode missing for item ${item}`);
const hasAmount = Number.isFinite(Number(amount));
if (hasAmount) {
return new Uint8Array([
id,
Math.max(0, Math.min(255, Number(amount) | 0))
]);
}
return new Uint8Array([id]);
}
function worldPointToMousePacket(x, y) {
const P = S.PacketIds || {};
const tr = S.renderTransform;
const scale = Number(tr && tr.scale);
const width = Number(tr && tr.canvasWidth) || innerWidth;
const height = Number(tr && tr.canvasHeight) || innerHeight;
const nodeX = Number(tr && tr.nodeX);
const nodeY = Number(tr && tr.nodeY);
if (
!Number.isFinite(Number(P.MOUSE)) ||
!Number.isFinite(scale) || scale <= 0 ||
!Number.isFinite(width) || width <= 0 ||
!Number.isFinite(height) || height <= 0 ||
!Number.isFinite(nodeX) || !Number.isFinite(nodeY) ||
!Number.isFinite(Number(x)) || !Number.isFinite(Number(y))
) {
return null;
}
const screenX = (Number(x) - nodeX) * scale + width / 2;
const screenY = (Number(y) - nodeY) * scale + height / 2;
if (!Number.isFinite(screenX) || !Number.isFinite(screenY)) return null;
// Same quantization as the updated native client:
// byte = screenCoordinate / canvasSize * 255.
const bx = Math.max(0, Math.min(255, Math.floor(screenX / width * 255)));
const by = Math.max(0, Math.min(255, Math.floor(screenY / height * 255)));
return new Uint8Array([Number(P.MOUSE) & 255, bx, by]);
}
function currentRealMousePacket() {
const P = S.PacketIds || {};
if (
S.lastNativeMousePacket instanceof Uint8Array &&
S.lastNativeMousePacket.length >= 3 &&
Number.isFinite(Number(P.MOUSE)) &&
S.lastNativeMousePacket[0] === (Number(P.MOUSE) & 255)
) {
return new Uint8Array(S.lastNativeMousePacket);
}
const mouse = currentWorldMouse();
return mouse ? worldPointToMousePacket(mouse.x, mouse.y) : null;
}
function sendTargetedPowerPacket(item, x, y, amount = null, reason = 'targeted-power') {
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) return false;
const targetMousePacket = worldPointToMousePacket(x, y);
if (!targetMousePacket) {
S.lastBlock = {
reason: 'target-mouse-transform-not-ready',
detail: reason,
item: Number(item),
x: Number(x),
y: Number(y),
t: now()
};
return false;
}
const restoreMousePacket = currentRealMousePacket();
const dropPacket = buildDropPacket(item, x, y, amount);
try {
// Protocol v2 no longer carries X/Y inside the power packet. The server
// uses its latest MOUSE state, so temporarily point the server mouse at
// the requested world target, drop, then restore the user's real mouse.
Reflect.apply(S.nativeSend, S.ws, [targetMousePacket]);
Reflect.apply(S.nativeSend, S.ws, [dropPacket]);
if (restoreMousePacket) {
Reflect.apply(S.nativeSend, S.ws, [restoreMousePacket]);
}
return true;
} catch (e) {
S.lastBlock = {
reason: 'targeted-power-send-failed',
detail: reason,
item: Number(item),
x: Number(x),
y: Number(y),
error: e && e.message ? e.message : String(e),
t: now()
};
return false;
}
}
function currentWorldMouse() {
const m = S.mouseWorld;
if (!m) return null;
const ageMs = now() - Number(m.t || 0);
if (
ageMs < 0 ||
ageMs > Math.max(0, Number(C.manualMouseFreshMs) || 30000) ||
!Number.isFinite(Number(m.x)) ||
!Number.isFinite(Number(m.y))
) {
return null;
}
return {
x: Number(m.x) | 0,
y: Number(m.y) | 0,
ageMs,
source: m.source || 'unknown'
};
}
function comboReferencePoint() {
const mouse = currentWorldMouse();
if (mouse) return mouse;
// When the panel button is clicked before a recent native MOUSE packet is
// available, use the user's largest verified own-cell as a safe reference.
const own = strictLiveOwnDropCell();
if (!own) return null;
return {
x: own.x | 0,
y: own.y | 0,
ageMs: 0,
source: 'largest-live-own-cell-fallback'
};
}
function livePlayerCellsForManualTarget(includeOwn = true) {
const players = pagePlayers();
const E = S.EntityTypes;
const playerType = E && Number(E.PLAYER);
const t = now();
const rows = [];
if (!players || !Number.isFinite(playerType)) return rows;
for (const key of Object.keys(players)) {
const player = players[key];
const owner = Number(player && player.id);
if (!player || !Number.isFinite(owner)) continue;
if (!includeOwn && owner === Number(S.mainPlayerId)) continue;
if (!player.cells || typeof player.cells[Symbol.iterator] !== 'function') continue;
for (const c of player.cells) {
if (!c || c.destroyed) continue;
if (Number(c.owner) !== owner) continue;
if (Number(c.type) !== playerType) continue;
const x = Number.isFinite(Number(c.nx)) ? Number(c.nx) : Number(c.x);
const y = Number.isFinite(Number(c.ny)) ? Number(c.ny) : Number(c.y);
const size = Number.isFinite(Number(c.nSize))
? Number(c.nSize)
: Number.isFinite(Number(c.originalSize))
? Number(c.originalSize)
: Number(c.size);
const updatedAt = Number(c.updateTime || c.time || 0);
const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;
if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
if (!Number.isFinite(size) || size <= 0) continue;
if (
updatedAt > 0 &&
C.comboTargetMaxCellAgeMs > 0 &&
ageMs > C.comboTargetMaxCellAgeMs
) {
continue;
}
rows.push({
raw: c,
player,
id: Number(c.id) || 0,
owner,
x,
y,
size,
ageMs
});
}
}
return rows;
}
function pickNearestOwnCellToMouse(mouse) {
if (!mouse || !S.mainPlayerId) return null;
let best = null;
let bestDist2 = Infinity;
for (const cell of livePlayerCellsForManualTarget(true)) {
// Strict own-cell-only guard. Never allow another player's cell.
if (Number(cell.owner) !== Number(S.mainPlayerId)) continue;
const dx = cell.x - mouse.x;
const dy = cell.y - mouse.y;
const dist2 = dx * dx + dy * dy;
if (
dist2 < bestDist2 ||
(
dist2 === bestDist2 &&
(!best || cell.size > best.size)
)
) {
best = cell;
bestDist2 = dist2;
}
}
if (!best) return null;
return {
...best,
mouseDistance: Math.sqrt(bestDist2),
selection: 'nearest-live-own-cell-to-mouse',
ownCellOnly: true
};
}
function pickNearestOpponentCellToMouse(mouse) {
if (!mouse || !S.mainPlayerId) return null;
let best = null;
let bestDist2 = Infinity;
for (const cell of livePlayerCellsForManualTarget(false)) {
if (Number(cell.owner) === Number(S.mainPlayerId)) continue;
const dx = Number(cell.x) - Number(mouse.x);
const dy = Number(cell.y) - Number(mouse.y);
const dist2 = dx * dx + dy * dy;
if (
dist2 < bestDist2 ||
(
dist2 === bestDist2 &&
best &&
Number(cell.ageMs || 0) < Number(best.ageMs || 0)
)
) {
best = cell;
bestDist2 = dist2;
}
}
if (!best) return null;
return {
...best,
mouseDistance: Math.sqrt(bestDist2),
opponentNearestDistance: Math.sqrt(bestDist2),
selection: 'nearest-live-enemy-cell-to-mouse',
nearestCellOnly: true
};
}
function largestLiveCellForOwner(owner) {
owner = Number(owner);
if (!Number.isFinite(owner) || owner === Number(S.mainPlayerId)) return null;
const candidates = livePlayerCellsForManualTarget(false)
.filter(cell => Number(cell.owner) === owner)
.sort((a, b) => (b.size - a.size) || (a.ageMs - b.ageMs));
return candidates[0] || null;
}
function manualItemCooldownMs(item) {
const learned = Number(S.powerCooldowns[Number(item)]);
if (Number.isFinite(learned) && learned >= 0) return learned;
return 1000;
}
function manualItemWaitMs(item) {
const lastAt = Number(S.manualItemLastSend[Number(item)] || 0);
const cooldownMs = manualItemCooldownMs(item);
if (!lastAt || cooldownMs <= 0) return 0;
return Math.max(0, cooldownMs - (now() - lastAt));
}
function recordManualItemSend(item) {
S.manualItemLastSend[Number(item)] = now();
}
function sendManualDrop(item, target, reason, amount = null, ignoreKnownCooldown = false) {
if (!S.mapsReady || !S.PacketIds || !S.EntityTypes) {
S.lastBlock = { reason: 'native-maps-not-ready', detail: reason, t: now() };
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.lastBlock = { reason: 'ws-not-ready-manual-drop', detail: reason, t: now() };
return false;
}
if (
!target ||
!Number.isFinite(Number(target.x)) ||
!Number.isFinite(Number(target.y))
) {
S.lastBlock = { reason: 'manual-target-not-ready', detail: reason, t: now() };
return false;
}
const waitMs = manualItemWaitMs(item);
if (!ignoreKnownCooldown && waitMs > 0) {
S.lastBlock = {
reason: 'manual-item-known-cooldown',
detail: reason,
item: Number(item),
waitMs,
t: now()
};
return false;
}
try {
const ok = sendTargetedPowerPacket(
item,
Number(target.x),
Number(target.y),
amount,
reason
);
if (!ok) return false;
recordManualItemSend(item);
return true;
} catch (e) {
S.lastBlock = {
reason: 'manual-drop-send-failed',
detail: reason,
item: Number(item),
error: e && e.message ? e.message : String(e),
t: now()
};
return false;
}
}
function sendGrowth30Shield(reason = 'growth30-shield-no-mouse') {
const E = S.EntityTypes;
const P = S.PacketIds;
if (!S.mapsReady || !E || !P || !S.nativeSend) {
S.lastBlock = { reason: 'growth30-shield-runtime-not-ready', detail: reason, t: now() };
updatePanel();
return false;
}
if (!S.ws || S.ws.readyState !== 1) {
S.lastBlock = { reason: 'growth30-shield-ws-not-ready', detail: reason, t: now() };
updatePanel();
return false;
}
const growthItem = Number(E.GROWTH);
const shieldItem = Number(E.SHIELD);
if (!Number.isFinite(growthItem) || !Number.isFinite(shieldItem)) {
S.lastBlock = {
reason: 'growth30-shield-item-map-missing',
detail: reason,
growthItem,
shieldItem,
t: now()
};
updatePanel();
return false;
}
const growthAvailable = autoItemAvailable(growthItem);
const shieldAvailable = autoItemAvailable(shieldItem);
let growthOk = false;
let shieldOk = false;
try {
// Deliberately NO mouse targeting:
// - no currentWorldMouse()
// - no worldPointToMousePacket()
// - no temporary MOUSE packet
// - no mouse restore
// Only the two native power-drop packets below.
if (growthAvailable) {
const growthPacket = buildDropPacket(growthItem, 0, 0, 30);
Reflect.apply(S.nativeSend, S.ws, [growthPacket]);
recordManualItemSend(growthItem);
growthOk = true;
}
if (shieldAvailable) {
const shieldPacket = buildDropPacket(shieldItem, 0, 0, null);
Reflect.apply(S.nativeSend, S.ws, [shieldPacket]);
recordManualItemSend(shieldItem);
shieldOk = true;
}
S.growthSends++;
S.lastGrowthSend = {
combo: 'growth30+shield',
amount: 30,
growthSent: growthOk,
shieldSent: shieldOk,
growthAvailable,
shieldAvailable,
mouseRead: false,
mousePacketSent: false,
mouseRestoreSent: false,
aimed: false,
target: null,
reason,
t: now()
};
S.lastDrop = {
combo: 'growth30+shield',
growthAmount: 30,
growthSent: growthOk,
shieldSent: shieldOk,
coordinateSource: 'none-no-mouse',
targetLocked: false,
mouseTargeted: false,
reason,
t: now()
};
if (!growthOk && !shieldOk) {
S.lastBlock = {
reason: 'growth30-shield-inventory-empty',
detail: reason,
growthInventory: knownInventoryCount(growthItem),
shieldInventory: knownInventoryCount(shieldItem),
t: now()
};
updatePanel();
return false;
}
updatePanel();
return true;
} catch (e) {
S.lastBlock = {
reason: 'growth30-shield-send-failed',
detail: reason,
error: e && e.message ? e.message : String(e),
t: now()
};
updatePanel();
return false;
}
}
function configuredAutoPelletWorld() {
const x = Number(C.autoPelletWorldX);
const y = Number(C.autoPelletWorldY);
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
return { x: x | 0, y: y | 0, ageMs: 0, source: 'configured-auto-pellet-world' };
}
function setAutoPelletWorld(x, y, source = 'manual-world-input') {
const nx = Number(x);
const ny = Number(y);
if (!Number.isFinite(nx) || !Number.isFinite(ny)) return false;
C.autoPelletWorldX = nx | 0;
C.autoPelletWorldY = ny | 0;
savePrefs();
S.lastAutoPellet = {
ok: true,
configuredOnly: true,
reason: 'auto-pellet-world-set',
x: C.autoPelletWorldX,
y: C.autoPelletWorldY,
coordinateSource: source,
t: now()
};
updatePanel(true);
return { x: C.autoPelletWorldX, y: C.autoPelletWorldY };
}
function captureAutoPelletWorldFromMouse(source = 'capture-world-from-mouse') {
const mouse = currentWorldMouse();
if (!mouse) {
S.lastAutoPellet = { ok: false, reason: 'world-mouse-not-ready', detail: source, t: now() };
updatePanel(true);
return false;
}
return setAutoPelletWorld(mouse.x, mouse.y, source);
}
function clearAutoPelletTimer() {
if (S.autoPelletTimer) {
clearTimeout(S.autoPelletTimer);
S.autoPelletTimer = null;
}
}
function scheduleAutoPellet(delayMs = C.autoPelletIntervalMs) {
clearAutoPelletTimer();
if (!C.autoPelletEnabled) return false;
const delay = Math.max(0, Number(delayMs) || 0);
S.autoPelletTimer = setTimeout(() => {
S.autoPelletTimer = null;
autoPelletTick('timer');
}, Math.ceil(delay));
return true;
}
function sendAutoPellet30(reason = 'auto-pellet-x30') {
const E = S.EntityTypes;
const fixedWorld = configuredAutoPelletWorld();
S.autoPelletRuns++;
if (!fixedWorld) {
S.lastAutoPellet = {
ok: false,
reason: 'auto-pellet-world-not-set',
detail: reason,
t: now()
};
updatePanel();
return false;
}
if (!E || !Number.isFinite(Number(E.GROWTH))) {
S.lastAutoPellet = {
ok: false,
reason: 'growth-item-map-missing',
detail: reason,
t: now()
};
updatePanel();
return false;
}
const item = Number(E.GROWTH);
const target = {
x: fixedWorld.x | 0,
y: fixedWorld.y | 0,
source: fixedWorld.source || 'configured-auto-pellet-world'
};
const amount = Math.max(1, Math.min(255, Number(C.autoPelletAmount) | 0));
const ok = sendManualDrop(item, target, reason, amount);
if (ok) {
S.autoPelletPackets++;
S.autoPelletTotal += amount;
S.lastAutoPellet = {
ok: true,
amount,
total: S.autoPelletTotal,
packets: S.autoPelletPackets,
x: target.x,
y: target.y,
mouseAgeMs: null,
coordinateSource: target.source,
fixedWorldTarget: true,
reason,
t: now()
};
S.lastDrop = {
item,
amount,
x: target.x,
y: target.y,
mouseTargeted: true,
autoPellet: true,
reason,
t: now()
};
} else {
S.lastAutoPellet = {
ok: false,
reason: S.lastBlock && S.lastBlock.reason || 'send-blocked',
detail: reason,
x: target.x,
y: target.y,
t: now()
};
}
updatePanel();
return ok;
}
function autoPelletTick(source = 'timer') {
if (!C.autoPelletEnabled) {
clearAutoPelletTimer();
return false;
}
const ok = sendAutoPellet30(`auto-pellet-${source}`);
scheduleAutoPellet(C.autoPelletIntervalMs);
return ok;
}
function setAutoPellet(value) {
C.autoPelletEnabled = !!value;
savePrefs();
clearAutoPelletTimer();
if (C.autoPelletEnabled) {
// If X/Y are still empty, capture the current world mouse ONCE.
if (!configuredAutoPelletWorld()) {
const captured = captureAutoPelletWorldFromMouse('auto-pellet-enable-capture');
if (!captured) {
C.autoPelletEnabled = false;
savePrefs();
updatePanel(true);
return false;
}
}
// Later mouse movement does not change the Auto Pellet target.
scheduleAutoPellet(0);
}
updatePanel(true);
return C.autoPelletEnabled;
}
function portalInventoryCount() {
const E = S.EntityTypes;
if (!E || !Number.isFinite(Number(E.PORTAL))) return null;
if (S.unlimitedInventory) return Infinity;
const count = Number(S.inventoryCounts[Number(E.PORTAL)]);
return Number.isFinite(count) ? count : null;
}
function livePortalNodeIds() {
const cells = pageCells();
const E = S.EntityTypes;
const out = new Set();
if (!cells || !E || !Number.isFinite(Number(E.PORTAL))) return out;
for (const key of Object.keys(cells)) {
const cell = cells[key];
if (!cell || cell.destroyed) continue;
if (Number(cell.type) !== Number(E.PORTAL)) continue;
const id = Number(cell.id);
out.add(Number.isFinite(id) && id ? id : String(key));
}
return out;
}
function countNewPortalNodes(baselineIds) {
const current = livePortalNodeIds();
let count = 0;
for (const id of current) {
if (!baselineIds.has(id)) count++;
}
return {
count,
current
};
}
function portalAcceptanceEvidence(startInventory, baselineIds, requiredCount) {
const currentInventory = portalInventoryCount();
const nodeEvidence = countNewPortalNodes(baselineIds);
const inventoryAccepted = (
Number.isFinite(startInventory) &&
Number.isFinite(currentInventory) &&
currentInventory <= startInventory - requiredCount
);
const nodeAccepted = nodeEvidence.count >= requiredCount;
return {
accepted: inventoryAccepted || nodeAccepted,
requiredCount,
startInventory,
currentInventory,
inventoryDelta: (
Number.isFinite(startInventory) &&
Number.isFinite(currentInventory)
) ? startInventory - currentInventory : null,
newPortalNodes: nodeEvidence.count,
inventoryAccepted,
nodeAccepted,
t: now()
};
}
function waitForPortalAcceptance(
token,
startInventory,
baselineIds,
requiredCount,
timeoutMs,
callback
) {
const startedAt = now();
const pollMs = Math.max(4, Number(C.comboPortalConfirmPollMs) || 12);
const poll = () => {
if (token !== S.comboToken) return;
const evidence = portalAcceptanceEvidence(
startInventory,
baselineIds,
requiredCount
);
if (evidence.accepted) {
callback(true, evidence);
return;
}
if (now() - startedAt >= timeoutMs) {
callback(false, evidence);
return;
}
S.comboSecondPortalTimer = setTimeout(poll, pollMs);
};
poll();
}
function targetWithSecondPortalOffset(target, reference) {
if (!target || !C.comboSecondPortalOffsetEnabled) return target;
const size = Math.max(0, Number(target.size) || 0);
const maxInside = Math.max(0, size * 0.62);
const requested = Math.max(
Number(C.comboSecondPortalOffsetMin) || 60,
size * (Number(C.comboSecondPortalOffsetSizeFactor) || 0.45)
);
const distance = Math.min(
Number(C.comboSecondPortalOffsetMax) || 240,
maxInside,
requested
);
if (!(distance > 0)) return target;
let dx = 0;
let dy = 0;
const raw = target.raw;
if (raw) {
const nx = Number(raw.nx);
const ny = Number(raw.ny);
const ox = Number(raw.ox);
const oy = Number(raw.oy);
if (
Number.isFinite(nx) &&
Number.isFinite(ny) &&
Number.isFinite(ox) &&
Number.isFinite(oy)
) {
dx = nx - ox;
dy = ny - oy;
}
}
// If the opponent is stationary, offset away from the reference point.
if (dx * dx + dy * dy < 1e-6 && reference) {
dx = Number(target.x) - Number(reference.x);
dy = Number(target.y) - Number(reference.y);
}
if (dx * dx + dy * dy < 1e-6) {
dx = 1;
dy = 0;
}
const length = Math.hypot(dx, dy) || 1;
const ux = dx / length;
const uy = dy / length;
return {
...target,
x: Number(target.x) + ux * distance,
y: Number(target.y) + uy * distance,
secondPortalOffset: distance,
secondPortalOffsetX: ux * distance,
secondPortalOffsetY: uy * distance
};
}
function comboInventoryStatus(needsPortal = false) {
const E = S.EntityTypes;
if (!E) {
return {
known: false,
enough: false,
needsPortal: !!needsPortal,
missing: ['entity-maps']
};
}
if (S.unlimitedInventory) {
return {
known: true,
unlimited: true,
enough: true,
needsPortal: !!needsPortal,
portal: Infinity,
virus: Infinity,
antiRec: Infinity,
freeze: Infinity,
missing: []
};
}
const portal = Number(S.inventoryCounts[Number(E.PORTAL)]);
const virus = Number(S.inventoryCounts[Number(E.VIRUS)]);
const antiRec = Number(S.inventoryCounts[Number(E.ANTIRECOMBINE)]);
const freeze = Number(S.inventoryCounts[Number(E.FREEZE)]);
const requiredValues = [virus, antiRec, freeze];
if (needsPortal) requiredValues.push(portal);
const known = requiredValues.every(Number.isFinite);
if (!known) {
return {
known: false,
unlimited: false,
enough: true,
needsPortal: !!needsPortal,
portal: Number.isFinite(portal) ? portal : null,
virus: Number.isFinite(virus) ? virus : null,
antiRec: Number.isFinite(antiRec) ? antiRec : null,
freeze: Number.isFinite(freeze) ? freeze : null,
missing: []
};
}
const missing = [];
if (needsPortal && portal < 1) missing.push(`PORTAL need 1, have ${portal}`);
if (virus < 1) missing.push(`VIRUS need 1, have ${virus}`);
if (antiRec < 1) missing.push(`ANTI-REC need 1, have ${antiRec}`);
if (freeze < 1) missing.push(`FREEZE need 1, have ${freeze}`);
return {
known: true,
unlimited: false,
enough: missing.length === 0,
needsPortal: !!needsPortal,
portal,
virus,
antiRec,
freeze,
missing
};
}
function runNextQueuedCombo() {
if (S.comboRunning || !S.comboQueue.length) return false;
const next = S.comboQueue.shift();
setTimeout(() => {
sendTargetedCombo(next.reason || 'queued-targeted-combo');
}, 0);
return true;
}
function finishComboCycle() {
S.comboRunning = false;
S.comboSecondPortalTimer = null;
updatePanel();
runNextQueuedCombo();
}
function cancelTargetedCombo(message = '') {
S.comboToken++;
if (S.comboSecondPortalTimer) {
clearTimeout(S.comboSecondPortalTimer);
S.comboSecondPortalTimer = null;
}
S.comboRunning = false;
S.comboQueue.length = 0;
if (message) {
S.lastComboBlock = {
reason: message,
t: now()
};
}
updatePanel();
return true;
}
function sendTargetedCombo(reason = 'smart-drop') {
if (S.comboRunning) {
if (S.comboQueue.length >= Math.max(1, Number(C.comboQueueMax) || 3)) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'smart-drop-queue-full',
detail: reason,
queueLength: S.comboQueue.length,
t: now()
};
updatePanel();
return false;
}
S.comboQueue.push({ reason, t: now() });
S.hotkeyMessage = `Smart Drop queued (${S.comboQueue.length})`;
updatePanel();
return true;
}
const E = S.EntityTypes;
const reference = comboReferencePoint();
if (!reference) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'smart-drop-reference-not-ready',
detail: reason,
hint: 'Move the mouse in-game or spawn an own cell first',
t: now()
};
updatePanel();
return false;
}
if (
!E ||
!Number.isFinite(Number(E.VIRUS)) ||
!Number.isFinite(Number(E.ANTIRECOMBINE)) ||
!Number.isFinite(Number(E.FREEZE))
) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'smart-drop-item-map-missing',
detail: reason,
t: now()
};
updatePanel();
return false;
}
const selected = pickNearestOpponentCellToMouse(reference);
if (!selected) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'no-live-opponent-smart-drop',
detail: reason,
reference,
t: now()
};
updatePanel();
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'ws-not-ready-smart-drop',
detail: reason,
t: now()
};
updatePanel();
return false;
}
const token = ++S.comboToken;
S.comboRunning = true;
S.lastComboBlock = null;
// Keep the exact enemy cell nearest to the mouse.
// Never replace it with the owner's largest cell.
const latestTarget = selected;
const effectsAtSend = playerEffectsByOwner(selected.owner);
const usePortal = !!effectsAtSend.shield;
if (usePortal && !Number.isFinite(Number(E.PORTAL))) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'smart-drop-portal-map-missing',
detail: reason,
targetOwner: selected.owner,
effectsAtSend,
t: now()
};
S.lastBlock = S.lastComboBlock;
finishComboCycle();
return false;
}
const inventory = comboInventoryStatus(usePortal);
if (inventory.known && !inventory.enough) {
S.comboBlocks++;
S.lastComboBlock = {
reason: 'smart-drop-insufficient-inventory',
detail: reason,
targetOwner: selected.owner,
usePortal,
inventory,
t: now()
};
S.lastBlock = S.lastComboBlock;
S.hotkeyMessage = inventory.missing.join(' · ');
finishComboCycle();
return false;
}
const requestedOrder = usePortal
? ['PORTAL', 'ANTIRECOMBINE', 'FREEZE', 'VIRUS']
: ['ANTIRECOMBINE', 'FREEZE', 'VIRUS'];
const comboRecord = {
smartDrop: true,
shieldAtSend: usePortal,
effectsAtSend,
requestedOrder,
sentOrder: [],
packetsSent: 0,
portalPacketsSent: 0,
portalAcceptedCount: 0,
firstStagePacketsSent: 0,
targetOwner: latestTarget.owner,
targetId: latestTarget.id,
targetX: latestTarget.x | 0,
targetY: latestTarget.y | 0,
targetSize: Math.round(latestTarget.size || 0),
opponentNearestDistance: Math.round(selected.opponentNearestDistance || selected.mouseDistance || 0),
nearestCellMouseDistance: Math.round(selected.mouseDistance || 0),
targetSelection: selected.selection || 'nearest-live-enemy-cell-to-mouse',
referenceX: reference.x,
referenceY: reference.y,
referenceSource: reference.source,
inventoryAtStart: inventory,
reason,
startedAt: now(),
completedAt: 0
};
S.lastComboSend = comboRecord;
S.comboSends++;
const failSmartDrop = (blockReason, extra = {}) => {
S.comboBlocks++;
S.lastComboBlock = {
reason: blockReason,
detail: reason,
targetOwner: latestTarget.owner,
shieldAtSend: usePortal,
sentOrder: comboRecord.sentOrder.slice(),
...extra,
t: now()
};
S.lastBlock = S.lastComboBlock;
finishComboCycle();
return false;
};
if (token !== S.comboToken) {
return failSmartDrop('smart-drop-cancelled-before-send');
}
// Shield is checked immediately before this branch. Portal is never sent
// when the target's live Shield animation is OFF.
if (usePortal) {
const portalOk = sendManualDrop(
E.PORTAL,
latestTarget,
`${reason}-shield-portal`,
null,
true
);
if (!portalOk) return failSmartDrop('smart-drop-portal-send-failed');
comboRecord.sentOrder.push('PORTAL');
comboRecord.portalPacketsSent = 1;
comboRecord.packetsSent++;
}
const antiRecOk = sendManualDrop(
E.ANTIRECOMBINE,
latestTarget,
`${reason}-antirecombine`,
null,
true
);
if (!antiRecOk) return failSmartDrop('smart-drop-anti-rec-send-failed');
comboRecord.sentOrder.push('ANTIRECOMBINE');
comboRecord.packetsSent++;
const freezeOk = sendManualDrop(
E.FREEZE,
latestTarget,
`${reason}-freeze`,
null,
true
);
if (!freezeOk) return failSmartDrop('smart-drop-freeze-send-failed');
comboRecord.sentOrder.push('FREEZE');
comboRecord.packetsSent++;
const virusOk = sendManualDrop(
E.VIRUS,
latestTarget,
`${reason}-virus`,
null,
true
);
if (!virusOk) return failSmartDrop('smart-drop-virus-send-failed');
comboRecord.sentOrder.push('VIRUS');
comboRecord.packetsSent++;
comboRecord.firstStagePacketsSent = comboRecord.packetsSent;
comboRecord.completedAt = now();
S.lastDrop = {
item: usePortal
? 'SMART:PORTAL+ANTIRECOMBINE+FREEZE+VIRUS'
: 'SMART:ANTIRECOMBINE+FREEZE+VIRUS',
x: latestTarget.x | 0,
y: latestTarget.y | 0,
targetId: latestTarget.id,
targetOwner: latestTarget.owner,
targetLocked: true,
targetSelection: 'nearest-live-enemy-cell-to-mouse',
mouseDistance: Math.round(selected.mouseDistance || 0),
shieldAtSend: usePortal,
effectsAtSend,
reason,
t: now()
};
S.hotkeyMessage = usePortal
? 'Smart: nearest cell → Portal + Anti-Rec + Freeze + Virus'
: 'Smart: nearest cell → Anti-Rec + Freeze + Virus';
finishComboCycle();
return true;
}
function sendNativeUint8(op, reason = 'native-u8') {
if (!S.ws || S.ws.readyState !== 1) {
S.lastBlock = { reason: 'ws-not-open', detail: reason, t: now() };
return false;
}
if (!S.nativeSend) {
S.lastBlock = { reason: 'native-send-missing', detail: reason, t: now() };
return false;
}
if (!Number.isFinite(Number(op))) {
S.lastBlock = { reason: 'invalid-native-op', detail: reason, op, t: now() };
return false;
}
try {
Reflect.apply(S.nativeSend, S.ws, [new Uint8Array([Number(op) & 255])]);
return true;
} catch (e) {
S.lastBlock = {
reason: 'native-u8-send-failed',
detail: reason,
error: e && e.message ? e.message : String(e),
t: now()
};
return false;
}
}
function sendPushBurst(count = C.pushBurstCount, reason = 'hotkey-push15') {
if (!C.pushBurstEnabled || S.pushBurstRunning) return false;
const P = S.PacketIds;
const op = P && Number(P.USE_PUSH);
const burstCount = Math.max(1, Math.min(200, Number(count) | 0));
const intervalMs = Math.max(0, Number(C.pushBurstIntervalMs) || 10.5);
if (!S.mapsReady || !Number.isFinite(op)) {
S.lastBlock = {
reason: 'use-push-op-missing',
detail: reason,
t: now()
};
updatePanel();
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.lastBlock = {
reason: 'push-burst-ws-not-ready',
detail: reason,
t: now()
};
updatePanel();
return false;
}
S.pushBurstRunning = true;
S.pushBurstToken++;
const token = S.pushBurstToken;
const startedAt = performance.now();
let sent = 0;
let error = '';
const finish = () => {
if (token !== S.pushBurstToken) return;
if (S.pushBurstTimer) {
clearTimeout(S.pushBurstTimer);
S.pushBurstTimer = null;
}
const endedAt = performance.now();
S.pushBurstRunning = false;
S.pushBursts++;
S.pushPacketsSent += sent;
S.lastPushBurst = {
requested: burstCount,
sent,
op,
intervalMs,
reason,
elapsedMs: Math.max(0, endedAt - startedAt),
error,
t: now()
};
updatePanel();
};
const sendNext = () => {
if (token !== S.pushBurstToken) return;
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
error = 'ws-closed-during-burst';
S.lastBlock = {
reason: error,
detail: reason,
sent,
t: now()
};
finish();
return;
}
try {
Reflect.apply(S.nativeSend, S.ws, [new Uint8Array([op & 255])]);
sent++;
} catch (e) {
error = e && e.message ? e.message : String(e);
S.lastBlock = {
reason: 'push-burst-send-failed',
detail: reason,
sent,
error,
t: now()
};
finish();
return;
}
if (sent >= burstCount) {
finish();
return;
}
S.pushBurstTimer = setTimeout(sendNext, intervalMs);
};
// First Push is immediate; the remaining 14 are spaced by 10.5 ms.
sendNext();
return true;
}
function isTypingTarget(target) {
if (!target) return false;
const tag = String(target.tagName || '').toUpperCase();
return (
tag === 'INPUT' ||
tag === 'TEXTAREA' ||
tag === 'SELECT' ||
!!target.isContentEditable ||
!!target.closest?.('[contenteditable="true"]')
);
}
function hotkeyLabel(code) {
code = String(code || '');
if (!code) return 'SET';
if (/^Key[A-Z]$/.test(code)) return code.slice(3);
if (/^Digit[0-9]$/.test(code)) return code.slice(5);
if (/^Numpad[0-9]$/.test(code)) return `Num${code.slice(6)}`;
if (/^F([1-9]|1[0-2])$/.test(code)) return code;
const labels = {
Space: 'SPACE',
Enter: 'ENTER',
Tab: 'TAB',
Escape: 'ESC',
Backspace: 'BACK',
Delete: 'DEL',
ArrowUp: '↑',
ArrowDown: '↓',
ArrowLeft: '←',
ArrowRight: '→',
ShiftLeft: 'L-SHIFT',
ShiftRight: 'R-SHIFT',
ControlLeft: 'L-CTRL',
ControlRight: 'R-CTRL',
AltLeft: 'L-ALT',
AltRight: 'R-ALT',
BracketLeft: '[',
BracketRight: ']',
Semicolon: ';',
Quote: "'",
Comma: ',',
Period: '.',
Slash: '/',
Backslash: '\\',
Minus: '-',
Equal: '=',
Backquote: '`'
};
return labels[code] || code;
}
function isModifierOnlyCode(code) {
return (
code === 'ShiftLeft' ||
code === 'ShiftRight' ||
code === 'ControlLeft' ||
code === 'ControlRight' ||
code === 'AltLeft' ||
code === 'AltRight' ||
code === 'MetaLeft' ||
code === 'MetaRight'
);
}
function hotkeyMatches(event, code) {
return !!(
event &&
code &&
String(event.code || '') === String(code)
);
}
function stopHotkeyEvent(event) {
if (!event) return;
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
function fastShieldDropOnce(reason = 'fast-shield-main-cell') {
S.fastShieldAttempts++;
const E = S.EntityTypes;
const item = E && Number(E.SHIELD);
if (!Number.isFinite(item)) {
S.lastBlock = { reason: 'fast-shield-item-id-missing', detail: reason, t: now() };
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.lastBlock = { reason: 'fast-shield-ws-not-ready', detail: reason, t: now() };
return false;
}
// If inventory is known and empty, fail closed instead of sending useless packets.
if (!autoItemAvailable(item)) {
S.lastBlock = {
reason: 'fast-shield-inventory-empty',
detail: reason,
inventory: knownInventoryCount(item),
t: now()
};
return false;
}
// strictLiveOwnDropCell() filters owner === mainPlayerId, PLAYER type only,
// then sorts by size descending. candidates[0] is therefore the current
// main/largest own cell, never a secondary/smaller own cell and never mouse.
const cell = strictLiveOwnDropCell();
if (!cell) {
S.lastBlock = { reason: 'fast-shield-main-cell-missing', detail: reason, t: now() };
return false;
}
const ok = sendTargetedPowerPacket(
item,
cell.x | 0,
cell.y | 0,
null,
reason
);
if (!ok) return false;
S.fastShieldSends++;
S.lastFastShield = {
cellId: Number(cell.id) || 0,
owner: Number(cell.owner) || Number(S.mainPlayerId) || 0,
x: cell.x | 0,
y: cell.y | 0,
size: Math.round(Number(cell.size) || 0),
intervalMs: Math.max(1, Number(C.fastShieldIntervalMs) || 50),
target: 'largest-live-own-cell',
mouseTargetUsed: false,
t: now()
};
S.lastDrop = {
item,
x: cell.x | 0,
y: cell.y | 0,
cellId: Number(cell.id) || 0,
owner: Number(cell.owner) || Number(S.mainPlayerId) || 0,
cellSize: Math.round(Number(cell.size) || 0),
coordinateSource: 'fast-shield-largest-own-cell',
selfLocked: true,
reason,
t: now()
};
return true;
}
function stopFastShieldDrop(reason = 'release') {
if (S.fastShieldTimer) {
clearInterval(S.fastShieldTimer);
S.fastShieldTimer = null;
}
const wasRunning = S.fastShieldRunning;
S.fastShieldRunning = false;
if (wasRunning) {
S.hotkeyMessage = `Fast Shield stopped (${reason})`;
updatePanel(true);
}
return wasRunning;
}
function startFastShieldDrop(reason = 'hotkey-hold') {
if (S.fastShieldRunning) return true;
S.fastShieldRunning = true;
S.hotkeyMessage = 'Fast Shield ON · MAIN CELL · 50ms';
fastShieldDropOnce(`${reason}-immediate`);
const ms = Math.max(1, Math.round(Number(C.fastShieldIntervalMs) || 50));
S.fastShieldTimer = setInterval(
() => fastShieldDropOnce(`${reason}-repeat`),
ms
);
updatePanel(true);
return true;
}
function toggleFastShieldDrop(reason = 'hotkey-toggle') {
if (S.fastShieldRunning) {
stopFastShieldDrop(`${reason}-off`);
return false;
}
startFastShieldDrop(`${reason}-on`);
return true;
}
function hotkeyKindLabel(kind) {
const labels = {
shield: 'Shield',
fastShield: 'Fast Shield Main 50ms',
push: 'Push ×15',
growth: 'Growth ×30 + Shield',
pelletMouse: 'Auto Pellet Set @ Mouse',
combo: 'Ptl+Vrs+ARec+Ptl'
};
return labels[kind] || kind;
}
function hotkeyCodesByKind() {
return {
shield: String(C.shieldHotkeyCode || ''),
fastShield: String(C.fastShieldHotkeyCode || ''),
push: String(C.pushBurstHotkeyCode || ''),
growth: String(C.growthHotkeyCode || ''),
pelletMouse: String(C.autoPelletSetMouseHotkeyCode || ''),
combo: String(C.comboHotkeyCode || '')
};
}
function setHotkeyCode(kind, code) {
if (kind === 'shield') C.shieldHotkeyCode = code;
else if (kind === 'fastShield') C.fastShieldHotkeyCode = code;
else if (kind === 'push') C.pushBurstHotkeyCode = code;
else if (kind === 'growth') C.growthHotkeyCode = code;
else if (kind === 'pelletMouse') C.autoPelletSetMouseHotkeyCode = code;
else if (kind === 'combo') C.comboHotkeyCode = code;
else return false;
return true;
}
function beginHotkeyCapture(kind) {
if (!['shield', 'fastShield', 'push', 'growth', 'pelletMouse', 'combo'].includes(kind)) return false;
S.hotkeyCapture = kind;
S.hotkeyCaptureStartedAt = now();
S.hotkeyMessage = `Press a key for ${hotkeyKindLabel(kind)}…`;
updatePanel();
return true;
}
function cancelHotkeyCapture(message = '') {
S.hotkeyCapture = null;
S.hotkeyCaptureStartedAt = 0;
S.hotkeyMessage = message;
updatePanel();
return true;
}
function assignCapturedHotkey(kind, code) {
code = String(code || '');
if (!code || isModifierOnlyCode(code)) {
S.hotkeyMessage = 'Modifier-only key is not supported';
updatePanel();
return false;
}
const duplicate = Object.entries(hotkeyCodesByKind()).find(
([otherKind, otherCode]) =>
otherKind !== kind &&
otherCode &&
otherCode === code
);
if (duplicate) {
S.hotkeyMessage =
`Key ${hotkeyLabel(code)} is already used by ${hotkeyKindLabel(duplicate[0])}`;
updatePanel();
return false;
}
if (!setHotkeyCode(kind, code)) return false;
S.hotkeyMessage = `${hotkeyKindLabel(kind)} key: ${hotkeyLabel(code)}`;
S.hotkeyCapture = null;
S.hotkeyCaptureStartedAt = 0;
savePrefs();
updatePanel();
return true;
}
function clearHotkey(kind) {
if (!setHotkeyCode(kind, '')) return false;
S.hotkeyMessage = `${hotkeyKindLabel(kind)} key cleared`;
savePrefs();
updatePanel();
return true;
}
function onGlobalHotkey(event) {
if (!event || event.repeat) return;
// Key assignment mode always has first priority.
if (S.hotkeyCapture) {
stopHotkeyEvent(event);
if (event.code === 'Escape') {
cancelHotkeyCapture('Key setting cancelled');
return;
}
if (event.code === 'Backspace' || event.code === 'Delete') {
const kind = S.hotkeyCapture;
S.hotkeyCapture = null;
clearHotkey(kind);
return;
}
assignCapturedHotkey(S.hotkeyCapture, event.code);
return;
}
if (event.ctrlKey || event.altKey || event.metaKey) return;
if (isTypingTarget(event.target) || isTypingTarget(document.activeElement)) return;
if (hotkeyMatches(event, C.fastShieldHotkeyCode)) {
if (C.fastShieldHotkeyStopGameKey) stopHotkeyEvent(event);
const enabled = toggleFastShieldDrop('hotkey-fast-shield');
S.hotkeyMessage = `Fast Shield ${enabled ? 'ON' : 'OFF'} · MAIN · ${C.fastShieldIntervalMs}ms`;
updatePanel(true);
return;
}
if (hotkeyMatches(event, C.shieldHotkeyCode)) {
if (C.shieldHotkeyStopGameKey) stopHotkeyEvent(event);
const enabled = setAutoShield(!C.autoShield);
S.hotkeyMessage = `Auto Shield ${enabled ? 'ON' : 'OFF'}`;
// setAutoShield already schedules an immediate Shield when enabled and
// the player currently has no Shield.
updatePanel();
return;
}
if (
C.pushBurstEnabled &&
hotkeyMatches(event, C.pushBurstHotkeyCode)
) {
if (C.pushBurstStopGameKey) stopHotkeyEvent(event);
sendPushBurst(C.pushBurstCount, 'hotkey-push15');
return;
}
if (hotkeyMatches(event, C.growthHotkeyCode)) {
if (C.growthHotkeyStopGameKey) stopHotkeyEvent(event);
sendGrowth30Shield('hotkey-growth30-shield');
return;
}
if (hotkeyMatches(event, C.autoPelletSetMouseHotkeyCode)) {
if (C.autoPelletSetMouseHotkeyStopGameKey) stopHotkeyEvent(event);
const captured = captureAutoPelletWorldFromMouse('hotkey-set-at-mouse');
if (captured) {
S.hotkeyMessage = `Auto Pellet target: X ${captured.x} · Y ${captured.y}`;
updatePanel(true);
}
return;
}
if (hotkeyMatches(event, C.comboHotkeyCode)) {
if (C.comboHotkeyStopGameKey) stopHotkeyEvent(event);
sendTargetedCombo('hotkey-smart-drop');
}
}
function itemCooldownMs(item) {
const learned = Number(S.powerCooldowns[Number(item)]);
if (Number.isFinite(learned) && learned > 0) return learned;
if (S.EntityTypes && Number(item) === Number(S.EntityTypes.ANTIRECOMBINE)) {
return C.viewAntiRecFallbackLockMs;
}
if (S.EntityTypes && Number(item) === Number(S.EntityTypes.FREEZE)) {
return C.viewFreezeFallbackLockMs;
}
return 1000;
}
function liveOwnCellsForView() {
const player = mainPlayer();
const E = S.EntityTypes;
const out = [];
if (!player || !player.cells || !E) return out;
for (const c of player.cells) {
if (!c || c.destroyed) continue;
if (Number(c.owner) !== Number(S.mainPlayerId)) continue;
if (Number(c.type) !== Number(E.PLAYER)) continue;
const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
const size = Number.isFinite(c.nSize)
? Number(c.nSize)
: Number.isFinite(c.originalSize)
? Number(c.originalSize)
: Number(c.size);
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(size) || size <= 0) {
continue;
}
out.push({
raw: c,
id: Number(c.id) || 0,
owner: Number(c.owner) || 0,
x,
y,
size
});
}
return out;
}
function playerEffectsByOwner(owner) {
const players = pagePlayers();
const player = players && players[owner];
const F = S.PlayerFlags || {};
const animations = player && Number.isFinite(Number(player.animations))
? Number(player.animations)
: 0;
return {
animations,
antiRec: !!(animations & Number(F.ANIMATION_ANTI_RECOMBINE || 0)),
frozen: !!(animations & Number(F.ANIMATION_FROZEN || 0)),
antiFreeze: !!(animations & Number(F.ANIMATION_ANTI_FREEZE || 0)),
shield: !!(animations & Number(F.ANIMATION_SHIELD || 0))
};
}
function liveEnemyCells() {
const players = pagePlayers();
const E = S.EntityTypes;
const playerType = E && Number(E.PLAYER);
const t = now();
const rows = [];
if (!players || !Number.isFinite(playerType) || !S.mainPlayerId) return rows;
for (const key of Object.keys(players)) {
const player = players[key];
if (!player || Number(player.id) === Number(S.mainPlayerId)) continue;
if (!player.cells || typeof player.cells[Symbol.iterator] !== 'function') continue;
for (const c of player.cells) {
if (!c || c.destroyed) continue;
if (Number(c.owner) === Number(S.mainPlayerId)) continue;
if (Number(c.owner) !== Number(player.id)) continue;
if (Number(c.type) !== playerType) continue;
const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
const size = Number.isFinite(c.nSize)
? Number(c.nSize)
: Number.isFinite(c.originalSize)
? Number(c.originalSize)
: Number(c.size);
const updatedAt = Number(c.updateTime || 0);
const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;
if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
if (!Number.isFinite(size) || size < C.viewMinEnemySize) continue;
if (updatedAt > 0 && ageMs > C.viewMaxCellAgeMs) continue;
rows.push({
raw: c,
player,
id: Number(c.id) || 0,
owner: Number(c.owner) || 0,
x,
y,
size,
ageMs,
effects: playerEffectsByOwner(c.owner)
});
}
}
return rows;
}
function sampleEnemyMotion(cell, sampleAt = now()) {
const key = `${cell.owner}:${cell.id}`;
const prev = S.viewMotion.get(key);
let vx = 0;
let vy = 0;
let source = 'none';
const raw = cell.raw;
// main_out.js stores the previous rendered/server position in ox/oy before
// assigning the new nx/ny from UPDATE_NODES. This gives us a direction on
// the very first scan after the packet, without waiting for a second sample.
if (
raw &&
Number.isFinite(Number(raw.nx)) &&
Number.isFinite(Number(raw.ny)) &&
Number.isFinite(Number(raw.ox)) &&
Number.isFinite(Number(raw.oy))
) {
const dx = Number(raw.nx) - Number(raw.ox);
const dy = Number(raw.ny) - Number(raw.oy);
const mag = Math.hypot(dx, dy);
if (mag >= 0.05) {
// Scale to a velocity-like value. Only direction is critical; thresholds
// in this sensitive build are deliberately very low.
vx = dx * 60;
vy = dy * 60;
source = 'server-nx-ox';
}
}
// Fallback to two-sample velocity if the current server delta is zero.
if (source === 'none' && prev) {
const dt = sampleAt - prev.t;
if (dt >= 4 && dt <= 1200) {
vx = (cell.x - prev.x) * 1000 / dt;
vy = (cell.y - prev.y) * 1000 / dt;
if (Math.hypot(vx, vy) >= 0.05) source = 'sample-delta';
}
}
S.viewMotion.set(key, { x: cell.x, y: cell.y, t: sampleAt });
if (S.viewMotion.size > 600) {
for (const [k, rec] of S.viewMotion) {
if (!rec || sampleAt - rec.t > 5000) S.viewMotion.delete(k);
}
}
return { vx, vy, source };
}
function strictEnemyDropTarget(target) {
if (!target || !target.raw || !S.mainPlayerId) return null;
const c = target.raw;
const players = pagePlayers();
const E = S.EntityTypes;
const owner = Number(c.owner);
const player = players && players[owner];
if (!player || owner === Number(S.mainPlayerId)) return null;
if (!player.cells || !player.cells.has(c)) return null;
if (c.destroyed || Number(c.type) !== Number(E.PLAYER)) return null;
const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
const size = Number.isFinite(c.nSize)
? Number(c.nSize)
: Number.isFinite(c.originalSize)
? Number(c.originalSize)
: Number(c.size);
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(size) || size <= 0) {
return null;
}
return {
raw: c,
player,
id: Number(c.id) || 0,
owner,
x,
y,
size,
effects: playerEffectsByOwner(owner)
};
}
function isAntiRecViewItem(item) {
return !!(
S.EntityTypes &&
Number(item) === Number(S.EntityTypes.ANTIRECOMBINE)
);
}
function isFreezeViewItem(item) {
return !!(
S.EntityTypes &&
Number(item) === Number(S.EntityTypes.FREEZE)
);
}
function viewLockKey(item, target) {
const itemId = Number(item);
const owner = Number(target && target.owner) || 0;
// Locks follow the opponent owner, not an individual split cell.
if (isAntiRecViewItem(itemId)) return `anti-rec-player:${owner}`;
if (isFreezeViewItem(itemId)) return `freeze-player:${owner}`;
return `${itemId}:${owner}:${Number(target && target.id) || 0}`;
}
function viewItemIntervalMs(item) {
if (isAntiRecViewItem(item)) {
return Math.max(1, Number(C.viewAntiRecPerPlayerLockMs) || 460);
}
if (isFreezeViewItem(item)) {
return Math.max(1, Number(C.viewFreezePerPlayerLockMs) || 2900);
}
return itemCooldownMs(item);
}
function viewTargetLockMs(item) {
return viewItemIntervalMs(item);
}
function viewItemReady(item, target) {
const t = now();
const intervalMs = viewItemIntervalMs(item);
const key = viewLockKey(item, target);
const targetAt = Number(S.viewTargetLocks.get(key) || 0);
const globalAt = Number(S.viewGlobalLocks.get(Number(item)) || 0);
// The global lock guarantees the requested overall packet rate even when
// the mouse moves quickly between different opponents.
return (
t - globalAt >= intervalMs &&
t - targetAt >= intervalMs
);
}
function viewItemRemainingMs(item, target, at = now()) {
const intervalMs = viewItemIntervalMs(item);
const key = viewLockKey(item, target);
const targetAt = Number(S.viewTargetLocks.get(key) || 0);
const globalAt = Number(S.viewGlobalLocks.get(Number(item)) || 0);
return Math.max(
0,
intervalMs - (at - globalAt),
intervalMs - (at - targetAt)
);
}
function setViewItemLock(item, target) {
const t = now();
const key = viewLockKey(item, target);
S.viewGlobalLocks.set(Number(item), t);
if (key) S.viewTargetLocks.set(key, t);
}
function clampNumber(value, min, max) {
value = Number(value);
min = Number(min);
max = Number(max);
if (!Number.isFinite(value)) value = min;
if (!Number.isFinite(min)) min = 0;
if (!Number.isFinite(max)) max = min;
return Math.max(min, Math.min(max, value));
}
function newestEnemyDirection(verified, threat = null) {
const c = verified && verified.raw;
let dx = 0;
let dy = 0;
let source = 'none';
// Prefer the current UPDATE_NODES delta at send time.
if (
c &&
Number.isFinite(Number(c.nx)) &&
Number.isFinite(Number(c.ny)) &&
Number.isFinite(Number(c.ox)) &&
Number.isFinite(Number(c.oy))
) {
dx = Number(c.nx) - Number(c.ox);
dy = Number(c.ny) - Number(c.oy);
if (Math.hypot(dx, dy) >= 0.05) source = 'live-nx-minus-ox';
}
// Fallback to the vector captured when this threat was selected.
if (
source === 'none' &&
threat &&
Number.isFinite(Number(threat.moveVx)) &&
Number.isFinite(Number(threat.moveVy))
) {
dx = Number(threat.moveVx);
dy = Number(threat.moveVy);
if (Math.hypot(dx, dy) >= 0.05) source = 'selected-threat-vector';
}
const magnitude = Math.hypot(dx, dy);
if (!Number.isFinite(magnitude) || magnitude < 0.05) {
return {
ok: false,
dx: 0,
dy: 0,
ux: 0,
uy: 0,
magnitude: 0,
source: 'no-live-motion'
};
}
return {
ok: true,
dx,
dy,
ux: dx / magnitude,
uy: dy / magnitude,
magnitude,
source
};
}
function centerEnemyDropPoint(verified) {
const centerX = Number(verified && verified.x);
const centerY = Number(verified && verified.y);
return {
x: Number.isFinite(centerX) ? Math.round(centerX) : 0,
y: Number.isFinite(centerY) ? Math.round(centerY) : 0,
centerX: Number.isFinite(centerX) ? Math.round(centerX) : 0,
centerY: Number.isFinite(centerY) ? Math.round(centerY) : 0,
leadDistance: 0,
forward: false,
direction: {
ok: false,
ux: 0,
uy: 0,
magnitude: 0,
source: 'exact-target-center'
}
};
}
function forwardEnemyDropPoint(verified, threat = null) {
const centerX = Number(verified.x);
const centerY = Number(verified.y);
const direction = newestEnemyDirection(verified, threat);
if (!C.viewForwardDropEnabled || !direction.ok) {
return {
x: centerX | 0,
y: centerY | 0,
centerX: centerX | 0,
centerY: centerY | 0,
leadDistance: 0,
forward: false,
direction
};
}
const speed = threat && Number.isFinite(Number(threat.speed))
? Math.max(0, Number(threat.speed))
: direction.magnitude * 60;
const leadDistance = clampNumber(
Number(C.viewForwardDropBase || 0) +
Number(verified.size || 0) * Number(C.viewForwardDropSizeFactor || 0) +
speed * Number(C.viewForwardDropSpeedFactor || 0),
Number(C.viewForwardDropMin || 0),
Number(C.viewForwardDropMax || 0)
);
const x = centerX + direction.ux * leadDistance;
const y = centerY + direction.uy * leadDistance;
if (
!Number.isFinite(x) ||
!Number.isFinite(y) ||
Math.abs(x) > 200000 ||
Math.abs(y) > 200000
) {
return {
x: centerX | 0,
y: centerY | 0,
centerX: centerX | 0,
centerY: centerY | 0,
leadDistance: 0,
forward: false,
direction: { ...direction, source: 'invalid-forward-point-fallback' }
};
}
return {
x: Math.round(x),
y: Math.round(y),
centerX: centerX | 0,
centerY: centerY | 0,
leadDistance: Math.round(leadDistance),
forward: true,
direction
};
}
function sendViewItem(item, target, kind, threat, source) {
const verified = strictEnemyDropTarget(target);
if (!verified) {
S.viewBlocks++;
S.lastViewBlock = {
reason: 'view-target-not-live-enemy',
kind,
source,
t: now()
};
return false;
}
if (
isFreezeViewItem(item) &&
verified.effects &&
verified.effects.antiFreeze
) {
S.viewBlocks++;
S.lastViewBlock = {
reason: 'freeze-target-has-antifreeze',
kind,
source,
item: Number(item),
targetId: verified.id,
targetOwner: verified.owner,
effects: verified.effects,
t: now()
};
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.viewBlocks++;
S.lastViewBlock = {
reason: 'view-ws-not-ready',
kind,
source,
t: now()
};
return false;
}
if (!autoItemAvailable(item)) {
S.viewBlocks++;
S.lastViewBlock = {
reason: 'view-item-inventory-empty',
kind,
source,
item: Number(item),
inventory: knownInventoryCount(item),
targetId: verified.id,
targetOwner: verified.owner,
t: now()
};
return false;
}
if (!viewItemReady(item, verified)) {
S.lastViewBlock = {
reason: 'view-item-cooldown-lock',
kind,
source,
item,
globalCooldownMs: viewItemIntervalMs(item),
targetCooldownMs: viewTargetLockMs(item),
targetLockKey: viewLockKey(item, verified),
targetId: verified.id,
targetOwner: verified.owner,
t: now()
};
return false;
}
try {
const dropPoint = C.viewCenterDropOnly
? centerEnemyDropPoint(verified)
: forwardEnemyDropPoint(verified, threat);
const ok = sendTargetedPowerPacket(
item,
dropPoint.x | 0,
dropPoint.y | 0,
null,
`view-${kind}-${source}`
);
if (!ok) {
S.viewBlocks++;
S.lastViewBlock = {
reason: 'view-targeted-send-failed',
kind,
source,
item: Number(item),
targetId: verified.id,
targetOwner: verified.owner,
t: now()
};
return false;
}
setViewItemLock(item, verified);
const rec = {
kind,
item: Number(item),
x: dropPoint.x | 0,
y: dropPoint.y | 0,
targetCenterX: dropPoint.centerX | 0,
targetCenterY: dropPoint.centerY | 0,
forwardDrop: !!dropPoint.forward,
leadDistance: dropPoint.leadDistance | 0,
directionSource: dropPoint.direction && dropPoint.direction.source || '',
directionX: dropPoint.direction ? Number(dropPoint.direction.ux.toFixed(4)) : 0,
directionY: dropPoint.direction ? Number(dropPoint.direction.uy.toFixed(4)) : 0,
targetId: verified.id,
targetOwner: verified.owner,
targetSize: Math.round(verified.size),
targetLocked: true,
source,
threat,
effectsBefore: verified.effects,
cooldownMs: viewItemIntervalMs(item),
targetCooldownMs: viewTargetLockMs(item),
targetLockKey: viewLockKey(item, verified),
t: now()
};
S.viewDrops++;
S.lastViewDrop = rec;
S.lastDrop = rec;
queueMicrotask(updatePanel);
return true;
} catch (e) {
S.viewBlocks++;
S.lastViewBlock = {
reason: 'view-send-failed',
kind,
source,
error: e && e.message ? e.message : String(e),
t: now()
};
return false;
}
}
function threatAgainstOwn(enemy, own, motion) {
const dx = own.x - enemy.x;
const dy = own.y - enemy.y;
const d = Math.sqrt(dx * dx + dy * dy);
if (!Number.isFinite(d) || d < 1 || d > C.viewRange) return null;
const vx = Number(motion.vx) || 0;
const vy = Number(motion.vy) || 0;
const speed = Math.sqrt(vx * vx + vy * vy);
const closing = speed > 0 ? (vx * dx + vy * dy) / d : 0;
const aimDot = speed > 0 ? closing / speed : 0;
// Distance from the own-cell center to the enemy's forward movement ray.
// This approximates "the opponent moved their mouse toward/near me".
const forwardProjection = speed > 0 ? (vx * dx + vy * dy) / speed : 0;
const perpendicularSq = Math.max(0, d * d - forwardProjection * forwardProjection);
const perpendicularDist = Math.sqrt(perpendicularSq);
const aimLineLimit = own.size + enemy.size + C.viewAimLineExtra;
const aimLineNear =
speed >= C.viewAimMinSpeed &&
closing >= C.viewAimClosingMin &&
aimDot >= C.viewAimDotMin &&
forwardProjection >= C.viewAimLineForwardMin &&
perpendicularDist <= aimLineLimit;
const predictAttackX = enemy.x + vx * (C.viewAttackPredictMs / 1000);
const predictAttackY = enemy.y + vy * (C.viewAttackPredictMs / 1000);
const predictApproachX = enemy.x + vx * (C.viewPredictMs / 1000);
const predictApproachY = enemy.y + vy * (C.viewPredictMs / 1000);
const predictedDist = Math.hypot(predictAttackX - own.x, predictAttackY - own.y);
const approachPredictedDist = Math.hypot(predictApproachX - own.x, predictApproachY - own.y);
const closeDist = own.size + enemy.size + C.viewAttackCloseMargin;
const hitDist = own.size + enemy.size + C.viewRushHitMargin;
const aiming =
aimLineNear ||
(
speed >= C.viewAimMinSpeed &&
closing >= C.viewAimClosingMin &&
aimDot >= C.viewAimDotMin &&
approachPredictedDist <= d - C.viewPredictGain
);
const tooClose = d <= closeDist;
const predictedHit =
speed >= C.viewRushMinSpeed &&
closing >= C.viewRushMinClosing &&
predictedDist <= hitDist + C.viewAttackPredictMargin;
const emergency =
d <= C.viewEmergencyMaxDist &&
speed >= C.viewEmergencySpeed &&
closing >= C.viewEmergencyClosing &&
aimDot >= C.viewEmergencyAimDot &&
(
aimLineNear ||
predictedDist <= hitDist + C.viewEmergencyPredictMargin
);
const freezeCloseLimit = Math.max(
C.viewFreezeCloseMinDist,
own.size + enemy.size + C.viewFreezeCloseExtra
);
const freezeClose = d <= freezeCloseLimit;
// Extreme mode:
// Any positive movement component toward an own-cell counts as aim intent.
// A wide forward corridor also catches slight diagonal/nearby aiming.
const anyTowardOwn =
speed >= C.viewAimMinSpeed &&
closing > 0 &&
forwardProjection >= 0 &&
aimDot >= -0.02;
const wideAimCorridor =
anyTowardOwn &&
perpendicularDist <= aimLineLimit * 1.35;
const instantAimDanger =
aimLineNear ||
wideAimCorridor ||
emergency ||
(anyTowardOwn && d <= C.viewEmergencyMaxDist);
return {
dist: d,
speed,
closing,
aimDot,
motionSource: motion.source || '',
moveVx: vx,
moveVy: vy,
forwardProjection,
perpendicularDist,
aimLineLimit,
aimLineNear,
anyTowardOwn,
wideAimCorridor,
predictedDist,
approachPredictedDist,
closeDist,
hitDist,
aiming,
tooClose,
predictedHit,
emergency,
// Extreme: both powers fire on the first positive toward-own movement.
antiRecDanger: instantAimDanger || aiming || predictedHit || tooClose,
freezeDanger: instantAimDanger || aiming || predictedHit || freezeClose,
freezeClose,
freezeCloseLimit,
ownId: own.id,
ownSize: own.size
};
}
function visibleEnemyTargetsByOwner() {
const cells = liveEnemyCells();
const byOwner = new Map();
for (const cell of cells) {
const current = byOwner.get(cell.owner);
// One representative target per visible player ID: use their largest
// current live cell, then freshest cell when sizes are equal.
if (
!current ||
cell.size > current.size ||
(
cell.size === current.size &&
cell.ageMs < current.ageMs
)
) {
byOwner.set(cell.owner, cell);
}
}
const targets = Array.from(byOwner.values()).sort(
(a, b) => (a.owner - b.owner) || (b.size - a.size)
);
S.visibleEnemyOwnerIds = targets.map(target => Number(target.owner));
S.visibleEnemyCount = S.visibleEnemyOwnerIds.length;
S.visibleEnemyUpdatedAt = now();
return targets;
}
function visibleTargetAllowed(kind, enemy) {
if (!enemy || !enemy.effects) return false;
if (kind === 'anti-rec') return true;
if (kind === 'freeze') {
// User rule: Freeze is allowed only while the selected opponent does NOT
// have the Anti-Freeze animation. Anti-Rec/Frozen do not block this loop.
return !enemy.effects.antiFreeze;
}
return false;
}
function pickMouseHoverViewTarget(kind, item, requireReady = true) {
const mouse = currentWorldMouse();
if (!mouse) return null;
const extraRadius = Math.max(
0,
Number(C.viewMouseTargetExtraRadius) || 0
);
let best = null;
for (const enemy of liveEnemyCells()) {
if (!visibleTargetAllowed(kind, enemy)) continue;
const dx = Number(enemy.x) - Number(mouse.x);
const dy = Number(enemy.y) - Number(mouse.y);
const distance = Math.hypot(dx, dy);
const radius = Math.max(1, Number(enemy.size) + extraRadius);
// Do not target a random nearest player elsewhere on screen. The cursor
// must actually be inside or very close to this live cell.
if (!Number.isFinite(distance) || distance > radius) continue;
const normalizedDistance = distance / Math.max(1, Number(enemy.size));
if (
!best ||
normalizedDistance < best.normalizedDistance ||
(
normalizedDistance === best.normalizedDistance &&
distance < best.distance
)
) {
best = {
target: enemy,
distance,
normalizedDistance,
radius
};
}
}
if (!best) return null;
if (requireReady && !viewItemReady(item, best.target)) return null;
return {
target: best.target,
nextCursor: 0,
threat: {
mode: 'mouse-hover-center',
visible: true,
cursorLocked: true,
exactCenter: true,
mouseX: mouse.x,
mouseY: mouse.y,
mouseAgeMs: Math.round(mouse.ageMs),
mouseDistance: Math.round(best.distance),
mouseRadius: Math.round(best.radius),
intervalMs: viewItemIntervalMs(item),
targetOwner: best.target.owner,
targetId: best.target.id,
targetSize: Math.round(best.target.size),
effects: best.target.effects
}
};
}
function clearAutoRecTimer() {
if (S.autoRecTimer) {
clearTimeout(S.autoRecTimer);
S.autoRecTimer = null;
}
S.autoRecDueAt = 0;
}
function sendAutoRecPacket(source = 'auto-rec') {
S.autoRecRuns++;
S.autoRecLastSource = source;
if (
!C.autoRecEnabled ||
!S.mapsReady ||
!S.PacketIds ||
!S.ws ||
S.ws.readyState !== 1 ||
!S.nativeSend ||
!S.mainPlayerId
) {
return false;
}
// EXACT native E-key behavior from main_out.js:
// sendUint8(PacketIds.USE_RECOMBINE)
// Do NOT use DROP_RECOMBINE here; DROP_RECOMBINE is the inventory
// drag/drop power packet and is a different action.
const opcode = Number(S.PacketIds.USE_RECOMBINE);
if (!Number.isFinite(opcode)) {
S.lastBlock = {
reason: 'auto-rec-use-recombine-opcode-missing',
detail: 'PacketIds.USE_RECOMBINE is unavailable',
t: now()
};
return false;
}
const packet = new Uint8Array([opcode & 255]);
try {
Reflect.apply(S.nativeSend, S.ws, [packet]);
S.autoRecSends++;
S.autoRecLastSendAt = now();
return true;
} catch (e) {
S.lastBlock = {
reason: 'auto-rec-use-recombine-send-failed',
detail: e && e.message ? e.message : String(e),
t: now()
};
return false;
}
}
function scheduleAutoRecLoop(delayMs = C.autoRecIntervalMs, source = 'auto-rec-schedule') {
if (!C.autoRecEnabled) {
clearAutoRecTimer();
return false;
}
const delay = Math.max(1, Number(delayMs) || 1);
const dueAt = now() + delay;
if (
S.autoRecTimer &&
Number(S.autoRecDueAt) > 0 &&
S.autoRecDueAt <= dueAt
) {
return false;
}
clearAutoRecTimer();
S.autoRecDueAt = dueAt;
S.autoRecLastSource = source;
S.autoRecTimer = setTimeout(() => {
S.autoRecTimer = null;
S.autoRecDueAt = 0;
sendAutoRecPacket(source);
if (C.autoRecEnabled) {
scheduleAutoRecLoop(C.autoRecIntervalMs, 'auto-rec-next');
}
}, Math.ceil(delay));
return true;
}
function setAutoRec(value) {
C.autoRecEnabled = !!value;
savePrefs();
if (C.autoRecEnabled) {
scheduleAutoRecLoop(1, 'auto-rec-enabled');
} else {
clearAutoRecTimer();
}
updatePanel(true);
return C.autoRecEnabled;
}
function clearAntiRecLoopTimer() {
if (S.antiRecLoopTimer) {
clearTimeout(S.antiRecLoopTimer);
S.antiRecLoopTimer = null;
}
S.antiRecLoopDueAt = 0;
}
function scheduleAntiRecLoop(delayMs = 0, source = 'schedule') {
if (!C.autoViewAntiRec || !C.antiRecContinuousPerPlayer) {
clearAntiRecLoopTimer();
return false;
}
const delay = Math.max(
Math.max(0, Number(C.antiRecLoopMinTimerMs) || 1),
Math.max(0, Number(delayMs) || 0)
);
const dueAt = now() + delay;
// Keep an already scheduled earlier run.
if (
S.antiRecLoopTimer &&
Number(S.antiRecLoopDueAt) > 0 &&
S.antiRecLoopDueAt <= dueAt
) {
return false;
}
clearAntiRecLoopTimer();
S.antiRecLoopDueAt = dueAt;
S.antiRecLoopLastSource = source;
S.antiRecLoopTimer = setTimeout(() => {
S.antiRecLoopTimer = null;
S.antiRecLoopDueAt = 0;
antiRecContinuousTick(source);
}, Math.ceil(delay));
return true;
}
function antiRecContinuousTick(source = 'timer') {
S.antiRecLoopRuns++;
S.antiRecLoopLastRunAt = now();
S.antiRecLoopLastSource = source;
if (!C.autoViewAntiRec || !C.antiRecContinuousPerPlayer) {
clearAntiRecLoopTimer();
return false;
}
if (
!S.mapsReady ||
!S.mainPlayerId ||
!S.ws ||
S.ws.readyState !== 1 ||
!S.nativeSend ||
!liveOwnCellsForView().length
) {
S.antiRecLoopLastVisibleCount = 0;
S.antiRecLoopLastSentOwners = [];
scheduleAntiRecLoop(C.antiRecLoopIdleMs, 'not-ready');
return false;
}
const visibleTargets = visibleEnemyTargetsByOwner();
S.antiRecLoopLastVisibleCount = visibleTargets.length;
const item = S.EntityTypes && S.EntityTypes.ANTIRECOMBINE;
if (!Number.isFinite(Number(item))) {
S.antiRecLoopLastSentOwners = [];
scheduleAntiRecLoop(C.antiRecLoopFailureRetryMs, 'anti-rec-map-missing');
return false;
}
const picked = pickMouseHoverViewTarget(
'anti-rec',
Number(item),
false
);
if (!picked) {
S.antiRecLoopLastSentOwners = [];
scheduleAntiRecLoop(C.antiRecLoopIdleMs, 'no-mouse-hover-target');
updatePanel();
return false;
}
S.lastViewTarget = {
kind: 'anti-rec',
targetId: picked.target.id,
targetOwner: picked.target.owner,
threat: picked.threat,
source,
t: now()
};
if (!viewItemReady(Number(item), picked.target)) {
S.antiRecLoopLastSentOwners = [];
scheduleAntiRecLoop(
Math.max(
Number(C.antiRecLoopMinTimerMs) || 1,
viewItemRemainingMs(Number(item), picked.target)
),
'mouse-target-anti-rec-due'
);
updatePanel();
return false;
}
const ok = sendViewItem(
Number(item),
picked.target,
'anti-rec-mouse-target',
picked.threat,
source
);
if (ok) {
S.antiRecLoopDrops++;
S.antiRecLoopLastSentOwners = [Number(picked.target.owner)];
scheduleAntiRecLoop(
viewItemIntervalMs(Number(item)),
'mouse-target-anti-rec-next'
);
} else {
S.antiRecLoopLastSentOwners = [];
scheduleAntiRecLoop(
C.antiRecLoopFailureRetryMs,
'mouse-target-anti-rec-retry'
);
}
updatePanel();
return ok;
}
function viewDefenseTick(source = 'poll', force = false) {
if (!C.autoViewAntiRec && !C.autoViewFreeze && !force) return false;
if (!S.mapsReady || !S.mainPlayerId || !S.ws || S.ws.readyState !== 1) return false;
// Keep automatic drops disabled while dead/spectating.
if (!liveOwnCellsForView().length) return false;
const t = now();
if (!force && t - S.lastViewScanAt < C.viewScanMs) return false;
S.lastViewScanAt = t;
S.viewScans++;
let sent = false;
visibleEnemyTargetsByOwner(); // refresh diagnostics only
if ((C.autoViewAntiRec || force) && !S.antiRecLoopTimer) {
scheduleAntiRecLoop(0, `${source}-mouse-update`);
}
if (C.autoViewFreeze || force) {
const item = S.EntityTypes && S.EntityTypes.FREEZE;
if (Number.isFinite(Number(item))) {
const picked = pickMouseHoverViewTarget(
'freeze',
Number(item),
true
);
if (picked) {
S.lastViewTarget = {
kind: 'freeze',
targetId: picked.target.id,
targetOwner: picked.target.owner,
threat: picked.threat,
source,
t
};
const ok = sendViewItem(
Number(item),
picked.target,
'freeze-mouse-target-no-antifreeze',
picked.threat,
source
);
if (ok) sent = true;
}
}
}
return sent;
}
function setAutoViewAntiRec(value) {
const previous = !!C.autoViewAntiRec;
C.autoViewAntiRec = !!value;
savePrefs();
if (C.autoViewAntiRec) {
if (!previous) {
for (const key of Array.from(S.viewTargetLocks.keys())) {
if (String(key).startsWith('anti-rec-player:')) {
S.viewTargetLocks.delete(key);
}
}
if (S.EntityTypes) {
S.viewGlobalLocks.delete(Number(S.EntityTypes.ANTIRECOMBINE));
}
}
scheduleAntiRecLoop(0, 'auto-anti-rec-enabled');
} else {
clearAntiRecLoopTimer();
S.antiRecLoopLastSentOwners = [];
}
if (!C.autoViewAntiRec && !C.autoViewFreeze) S.viewMotion.clear();
updatePanel(true);
return C.autoViewAntiRec;
}
function setAutoViewFreeze(value) {
const previous = !!C.autoViewFreeze;
C.autoViewFreeze = !!value;
savePrefs();
if (C.autoViewFreeze && !previous) {
for (const key of Array.from(S.viewTargetLocks.keys())) {
if (String(key).startsWith('freeze-player:')) {
S.viewTargetLocks.delete(key);
}
}
if (S.EntityTypes) {
S.viewGlobalLocks.delete(Number(S.EntityTypes.FREEZE));
}
}
if (!C.autoViewAntiRec && !C.autoViewFreeze) S.viewMotion.clear();
updatePanel();
return C.autoViewFreeze;
}
function trimShieldTimingHistory() {
const h = S.shieldTiming.history;
const max = Math.max(1, Number(C.shieldTimingHistoryMax) || 30);
if (h.length > max) h.splice(0, h.length - max);
}
function beginShieldOffCycle(atPerf, source = 'unknown') {
const st = S.shieldTiming;
const wall = now();
const perf = Number.isFinite(Number(atPerf)) ? Number(atPerf) : performance.now();
if (st.lastOffAtPerf > 0) {
st.lastClearIntervalMs = Math.max(0, perf - st.lastOffAtPerf);
}
if (st.lastOnAtPerf > 0) {
st.lastVisibleDurationMs = Math.max(0, perf - st.lastOnAtPerf);
}
st.lastOffAtPerf = perf;
st.lastOffAtWall = wall;
st.sendsThisCycle = 0;
st.lastClearToSendMs = null;
st.lastSendToOnMs = null;
st.lastClearToOnMs = null;
st.lastUnshieldedGapMs = null;
st.pendingCycle = {
offAtPerf: perf,
offAtWall: wall,
source,
firstSendAtPerf: 0,
firstSendAtWall: 0,
lastSendAtPerf: 0,
sends: 0
};
}
function recordShieldSend(atPerf, reason = '') {
const st = S.shieldTiming;
const perf = Number.isFinite(Number(atPerf)) ? Number(atPerf) : performance.now();
const wall = now();
if (st.lastSendAtPerf > 0) {
st.lastSendIntervalMs = Math.max(0, perf - st.lastSendAtPerf);
}
st.lastSendAtPerf = perf;
st.lastSendAtWall = wall;
st.lastSendReason = String(reason || '');
const cycle = st.pendingCycle;
if (cycle) {
cycle.sends++;
cycle.lastSendAtPerf = perf;
st.sendsThisCycle = cycle.sends;
if (!cycle.firstSendAtPerf) {
cycle.firstSendAtPerf = perf;
cycle.firstSendAtWall = wall;
st.lastClearToSendMs = Math.max(0, perf - cycle.offAtPerf);
}
}
}
function recordShieldOn(atPerf, source = 'unknown') {
const st = S.shieldTiming;
const wall = now();
const perf = Number.isFinite(Number(atPerf)) ? Number(atPerf) : performance.now();
if (st.lastOnAtPerf > 0) {
st.lastOnIntervalMs = Math.max(0, perf - st.lastOnAtPerf);
}
st.lastOnAtPerf = perf;
st.lastOnAtWall = wall;
if (st.lastOffAtPerf > 0) {
st.lastUnshieldedGapMs = Math.max(0, perf - st.lastOffAtPerf);
}
const cycle = st.pendingCycle;
if (cycle) {
const rec = {
source,
offAtWall: cycle.offAtWall,
firstSendAtWall: cycle.firstSendAtWall || 0,
onAtWall: wall,
clearToSendMs: cycle.firstSendAtPerf
? Math.max(0, cycle.firstSendAtPerf - cycle.offAtPerf)
: null,
sendToOnMs: cycle.firstSendAtPerf
? Math.max(0, perf - cycle.firstSendAtPerf)
: null,
clearToOnMs: Math.max(0, perf - cycle.offAtPerf),
sends: cycle.sends,
sendReason: st.lastSendReason
};
st.lastClearToSendMs = rec.clearToSendMs;
st.lastSendToOnMs = rec.sendToOnMs;
st.lastClearToOnMs = rec.clearToOnMs;
st.lastUnshieldedGapMs = rec.clearToOnMs;
st.cyclesCompleted++;
st.history.push(rec);
trimShieldTimingHistory();
st.pendingCycle = null;
st.sendsThisCycle = 0;
}
}
function resetShieldTiming() {
const fresh = {
lastOnAtPerf: 0,
lastOffAtPerf: 0,
lastSendAtPerf: 0,
lastOnAtWall: 0,
lastOffAtWall: 0,
lastSendAtWall: 0,
lastClearToSendMs: null,
lastSendToOnMs: null,
lastClearToOnMs: null,
lastVisibleDurationMs: null,
lastUnshieldedGapMs: null,
lastSendIntervalMs: null,
lastClearIntervalMs: null,
lastOnIntervalMs: null,
sendsThisCycle: 0,
cyclesCompleted: 0,
lastSendReason: '',
pendingCycle: null,
history: []
};
S.shieldTiming = fresh;
return fresh;
}
function knownInventoryCount(item) {
if (S.unlimitedInventory) return Infinity;
const count = Number(S.inventoryCounts[Number(item)]);
return Number.isFinite(count) ? count : null;
}
function autoItemAvailable(item) {
const count = knownInventoryCount(item);
// Unknown inventory: fail open because INIT/UPDATE_INVENTORY may not have
// arrived yet. Once known, never send automatic packets with count 0.
return count == null || count > 0;
}
function sendSelfItemAtVerifiedCell(item, verifiedCell, reason) {
if (!S.ws || S.ws.readyState !== 1) {
S.lastBlock = { reason: 'ws-not-open', detail: reason, t: now() };
return false;
}
if (!S.mapsReady || !S.PacketIds || !S.EntityTypes || !S.nativeSend) {
S.lastBlock = { reason: 'self-item-runtime-not-ready', detail: reason, t: now() };
return false;
}
if (
!verifiedCell ||
Number(verifiedCell.owner) !== Number(S.mainPlayerId) ||
!Number.isFinite(verifiedCell.x) ||
!Number.isFinite(verifiedCell.y)
) {
S.selfDropBlocks++;
S.lastBlock = {
reason: 'self-lock-verified-cell-invalid',
detail: reason,
verifiedCell,
t: now()
};
return false;
}
try {
const sentAtPerf = performance.now();
const targetedOk = sendTargetedPowerPacket(
item,
verifiedCell.x | 0,
verifiedCell.y | 0,
null,
reason
);
if (!targetedOk) {
S.selfDropBlocks++;
return false;
}
if (
S.EntityTypes &&
Number(item) === Number(S.EntityTypes.SHIELD)
) {
recordShieldSend(sentAtPerf, reason);
}
S.lastDrop = {
item,
x: verifiedCell.x | 0,
y: verifiedCell.y | 0,
cellId: verifiedCell.id,
owner: verifiedCell.owner,
cellSize: Math.round(verifiedCell.size),
cellAgeMs: Math.round(verifiedCell.ageMs || 0),
coordinateSource: verifiedCell.source || 'verified-own-cell',
selfLocked: true,
reason,
sentAtPerf,
t: now()
};
// UI work is deliberately deferred until after the packet event finishes.
queueMicrotask(updatePanel);
return true;
} catch (e) {
S.lastBlock = {
reason: 'self-item-send-failed',
detail: reason,
error: e && e.message ? e.message : String(e),
t: now()
};
return false;
}
}
function sendDrop(item, cell, reason) {
// Fail closed and verify once. The supplied cell is never trusted.
const verifiedCell = strictLiveOwnDropCell();
if (!verifiedCell) {
S.selfDropBlocks++;
S.lastBlock = {
reason: 'self-lock-no-live-own-cell',
detail: reason,
suppliedCell: cell
? { id: cell.id || 0, x: cell.x, y: cell.y }
: null,
t: now()
};
queueMicrotask(updatePanel);
return false;
}
return sendSelfItemAtVerifiedCell(item, verifiedCell, reason);
}
function sendShield(reason = 'auto-shield', cellOverride = null) {
const item = S.EntityTypes && S.EntityTypes.SHIELD;
if (!Number.isFinite(Number(item))) {
S.lastBlock = { reason: 'shield-item-id-missing', detail: reason, t: now() };
return false;
}
if (!autoItemAvailable(item)) {
S.lastBlock = {
reason: 'shield-inventory-empty',
detail: reason,
inventory: knownInventoryCount(item),
t: now()
};
return false;
}
const cell = cellOverride || strictLiveOwnDropCell();
if (!cell) {
S.lastBlock = { reason: 'shield-own-cell-missing', detail: reason, t: now() };
return false;
}
const ok = sendSelfItemAtVerifiedCell(Number(item), cell, reason);
if (ok) {
S.lastShieldSendAt = now();
S.shieldAttempts++;
}
return ok;
}
function sendAntiFreeze(reason = 'auto-anti-freeze', cellOverride = null) {
const item = S.EntityTypes && S.EntityTypes.ANTIFREEZE;
if (!Number.isFinite(Number(item))) {
S.lastBlock = { reason: 'anti-freeze-item-id-missing', detail: reason, t: now() };
return false;
}
if (!autoItemAvailable(item)) {
S.lastBlock = {
reason: 'anti-freeze-inventory-empty',
detail: reason,
inventory: knownInventoryCount(item),
t: now()
};
return false;
}
const cell = cellOverride || strictLiveOwnDropCell();
if (!cell) {
S.lastBlock = { reason: 'anti-freeze-own-cell-missing', detail: reason, t: now() };
return false;
}
const ok = sendSelfItemAtVerifiedCell(Number(item), cell, reason);
if (ok) {
S.lastAntiFreezeSendAt = now();
S.antiFreezeAttempts++;
}
return ok;
}
function shieldRetrySendGapMs() {
const item = S.EntityTypes && S.EntityTypes.SHIELD;
const learned = Number.isFinite(Number(item))
? Number(itemCooldownMs(Number(item)))
: 0;
return Math.max(
Math.max(20, Number(C.shieldRetryMinSendGapMs) || 100),
learned > 0
? learned + Math.max(0, Number(C.shieldRetryCooldownSafetyMs) || 12)
: 0
);
}
function queueShieldVerification(cycle, reason, delayMs = C.shieldRetryCheckMs) {
cancelTimer('shieldPending');
if (!C.autoShield || cycle !== S.shieldCycle) return false;
const delay = Math.max(1, Number(delayMs) || 20);
S.shieldPendingCreatedAt = now();
S.shieldPending = setTimeout(() => {
S.shieldPending = null;
S.shieldPendingCreatedAt = 0;
runShieldUntilEffect(cycle, reason);
}, delay);
return true;
}
function runShieldUntilEffect(cycle, reason) {
if (!C.autoShield || cycle !== S.shieldCycle) {
cancelTimer('shieldPending');
return false;
}
const current = flagsFromAnimations(liveAnimations());
// This is the only successful stop condition while Auto Shield is ON.
if (current.shield) {
cancelTimer('shieldPending');
S.shieldMissingSince = 0;
S.lastShieldWatchdogReason = 'shield-effect-confirmed';
return true;
}
const item = S.EntityTypes && S.EntityTypes.SHIELD;
const cell = fastOwnCell(C.shieldFastCacheMaxAgeMs);
if (
!Number.isFinite(Number(item)) ||
!cell ||
!S.ws ||
S.ws.readyState !== 1 ||
!S.mapsReady
) {
queueShieldVerification(
cycle,
`${reason}-waiting-runtime`,
Math.max(20, Number(C.shieldRetryCheckMs) || 20)
);
return false;
}
// Keep checking even when inventory is currently zero. The accepted Shield
// packet may have consumed the last item before its animation packet arrives.
if (!autoItemAvailable(Number(item))) {
S.lastShieldWatchdogReason = 'waiting-shield-effect-or-inventory';
queueShieldVerification(
cycle,
`${reason}-inventory-wait`,
Math.max(35, Number(C.shieldRetryCheckMs) || 20)
);
return false;
}
const t = now();
const sendGap = shieldRetrySendGapMs();
const elapsed = t - Number(S.lastShieldSendAt || 0);
if (S.shieldAttempts === 0 || elapsed >= sendGap) {
sendShield(
S.shieldAttempts === 0
? `${reason}-initial`
: `${reason}-retry-${S.shieldAttempts + 1}`,
cell
);
}
const remaining = Math.max(
1,
sendGap - (now() - Number(S.lastShieldSendAt || 0))
);
queueShieldVerification(
cycle,
reason,
Math.min(
Math.max(1, Number(C.shieldRetryCheckMs) || 20),
remaining
)
);
return false;
}
function fireShieldPacketDirect(reason, packetAt = now()) {
if (!C.autoShield) return false;
cancelTimer('shieldPending');
const cell = fastOwnCell(C.shieldFastCacheMaxAgeMs);
if (!cell) {
S.lastBlock = {
reason: 'packet-direct-own-cell-missing',
detail: reason,
t: now()
};
queueShieldVerification(S.shieldCycle, `${reason}-cell-fallback`, 1);
return false;
}
const sentAtPerf = performance.now();
const ok = sendShield(`${reason}-packet-direct`, cell);
if (ok) {
const reaction = Math.max(0, sentAtPerf - Number(packetAt || sentAtPerf));
S.lastShieldClearPacketAt = now();
S.lastShieldReactionMs = reaction;
S.lastShieldReactionUs = Math.round(reaction * 1000);
}
// Continue verification until the server reports ANIMATION_SHIELD.
queueShieldVerification(
S.shieldCycle,
`${reason}-until-effect`,
C.shieldRetryCheckMs
);
return ok;
}
function scheduleShield(reason) {
cancelTimer('shieldPending');
const cycle = S.shieldCycle;
const delay = Math.max(0, Number(C.shieldDelayMs) || 0);
if (delay > 0) {
queueShieldVerification(cycle, reason, delay);
} else {
runShieldUntilEffect(cycle, reason);
}
}
function fireAntiFreezePacketDirect(reason, packetAt = performance.now()) {
if (!C.autoAntiFreeze || !C.antiFreezePacketDirectEnabled) return false;
cancelTimer('antiFreezePending');
// O(1) validated own-cell hot path; strict synchronous scan is fallback.
const cell = fastOwnCell(C.antiFreezeFastCacheMaxAgeMs);
if (!cell) {
S.antiFreezePacketDirectFallbacks++;
S.lastBlock = {
reason: 'packet-direct-anti-freeze-own-cell-missing',
detail: reason,
t: now()
};
// Exceptional fallback only. Normal first send has no timer.
S.antiFreezePending = setTimeout(() => {
S.antiFreezePending = null;
scheduleAntiFreeze(`${reason}-cell-fallback`);
}, 0);
return false;
}
const sendAtPerf = performance.now();
const ok = sendAntiFreeze(`${reason}-packet-direct`, cell);
if (ok) {
const sourceAt = Number.isFinite(Number(packetAt))
? Number(packetAt)
: sendAtPerf;
const reactionMs = Math.max(0, sendAtPerf - sourceAt);
S.lastAntiFreezeFrozenPacketAt = now();
S.lastAntiFreezeReactionMs = reactionMs;
S.lastAntiFreezeReactionUs = Math.round(reactionMs * 1000);
S.antiFreezePacketDirectSends++;
}
// Retry does not delay the first packet; it only verifies later.
const cycle = S.freezeCycle;
if (ok && C.antiFreezeRetryMax > 0) {
S.antiFreezePending = setTimeout(() => {
S.antiFreezePending = null;
if (!C.autoAntiFreeze || cycle !== S.freezeCycle) return;
const verify = flagsFromAnimations(liveAnimations());
if (verify.frozen && !verify.antiFreeze) {
sendAntiFreeze(
`${reason}-packet-direct-verify-retry-${S.antiFreezeAttempts}`,
fastOwnCell(C.antiFreezeFastCacheMaxAgeMs)
);
}
}, Math.max(0, Number(C.antiFreezeRetryMs) || 0));
}
return ok;
}
function scheduleAntiFreeze(reason) {
cancelTimer('antiFreezePending');
const cycle = S.freezeCycle;
const run = () => {
if (!C.autoAntiFreeze || cycle !== S.freezeCycle) return;
const current = flagsFromAnimations(liveAnimations());
if (!current.frozen || current.antiFreeze || ownCells().length === 0) return;
sendAntiFreeze(reason);
if (S.antiFreezeAttempts <= C.antiFreezeRetryMax) {
S.antiFreezePending = setTimeout(() => {
const verify = flagsFromAnimations(liveAnimations());
if (verify.frozen && !verify.antiFreeze && cycle === S.freezeCycle) {
sendAntiFreeze(`${reason}-retry-${S.antiFreezeAttempts}`);
}
}, C.antiFreezeRetryMs);
}
};
if (C.antiFreezeDelayMs > 0) S.antiFreezePending = setTimeout(run, C.antiFreezeDelayMs);
else run();
}
function normalizeShieldBreakPlayerId(value) {
const id = Math.trunc(Number(value));
return Number.isFinite(id) && id >= 1 && id <= 65535 ? id : 0;
}
function clearShieldBreakTimer() {
if (S.shieldBreakTimer) {
clearTimeout(S.shieldBreakTimer);
S.shieldBreakTimer = null;
}
}
function resetShieldBreakCycle(reason = 'reset-shield-break') {
clearShieldBreakTimer();
S.shieldBreakTargetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
S.shieldBreakLastAnimations = null;
S.shieldBreakShieldOn = false;
S.shieldBreakPortalSent = false;
S.shieldBreakAttempts = 0;
S.shieldBreakDetectedAt = 0;
S.shieldBreakLastBlock = reason ? { reason, t: now() } : null;
}
function configuredShieldBreakPlayer() {
const id = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!id) return null;
const players = pagePlayers();
return players && players[id] ? players[id] : null;
}
function scheduleShieldBreakAttempt(delayMs = 0, reason = 'shield-break-schedule') {
clearShieldBreakTimer();
if (!C.shieldBreakEnabled || !S.shieldBreakShieldOn || S.shieldBreakPortalSent) return false;
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!targetId || targetId === Number(S.mainPlayerId)) return false;
const wait = Math.max(0, Number(delayMs) || 0);
S.shieldBreakTimer = setTimeout(() => {
S.shieldBreakTimer = null;
attemptShieldBreakPortal(reason);
}, Math.ceil(wait));
return true;
}
function shieldBreakEffectStillActive(targetId) {
if (!S.shieldBreakShieldOn) return false;
// The packet listener can run before main_out.js applies the same UPDATE_PLAYERS
// packet to PAGE.players. Trust the packet briefly; after that, require the
// client's live player animation state so a dead/disconnected target cannot
// leave a stale retry loop running forever.
const age = S.shieldBreakDetectedAt ? now() - S.shieldBreakDetectedAt : 0;
if (age < 120) return true;
const players = pagePlayers();
const player = players && players[targetId];
if (!player) {
if (age < 700) return true;
S.shieldBreakShieldOn = false;
S.shieldBreakLastBlock = { reason: 'shield-break-target-disappeared', targetId, t: now() };
return false;
}
if (Number.isFinite(Number(player.animations))) {
const stillOn = flagsFromAnimations(Number(player.animations)).shield;
if (!stillOn) {
S.shieldBreakShieldOn = false;
S.shieldBreakPortalSent = false;
S.shieldBreakAttempts = 0;
S.shieldBreakDetectedAt = 0;
S.shieldBreakLastBlock = { reason: 'shield-break-live-shield-ended', targetId, t: now() };
return false;
}
}
return true;
}
function attemptShieldBreakPortal(reason = 'shield-break') {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
const E = S.EntityTypes || {};
const portalItem = Number(E.PORTAL);
if (!C.shieldBreakEnabled || !targetId || !S.shieldBreakShieldOn || S.shieldBreakPortalSent) {
return false;
}
if (!shieldBreakEffectStillActive(targetId)) {
clearShieldBreakTimer();
updatePanel();
return false;
}
if (targetId === Number(S.mainPlayerId)) {
S.shieldBreakLastBlock = { reason: 'shield-break-target-is-self', targetId, t: now() };
return false;
}
if (!S.mapsReady || !Number.isFinite(portalItem)) {
S.shieldBreakLastBlock = { reason: 'shield-break-portal-map-not-ready', targetId, t: now() };
scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-map-retry`);
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.shieldBreakLastBlock = { reason: 'shield-break-ws-not-ready', targetId, t: now() };
scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-ws-retry`);
return false;
}
if (!autoItemAvailable(portalItem)) {
S.shieldBreakLastBlock = {
reason: 'shield-break-no-portal',
targetId,
inventory: knownInventoryCount(portalItem),
t: now()
};
scheduleShieldBreakAttempt(Math.max(80, C.shieldBreakRetryMs), `${reason}-inventory-retry`);
updatePanel();
return false;
}
const target = largestLiveCellForOwner(targetId);
if (!target) {
S.shieldBreakLastBlock = { reason: 'shield-break-target-cell-not-ready', targetId, t: now() };
scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-cell-retry`);
updatePanel();
return false;
}
const cooldownWait = manualItemWaitMs(portalItem);
if (cooldownWait > 0) {
S.shieldBreakLastBlock = {
reason: 'shield-break-portal-cooldown',
targetId,
waitMs: cooldownWait,
t: now()
};
scheduleShieldBreakAttempt(Math.max(C.shieldBreakRetryMs, cooldownWait + 1), `${reason}-cooldown-retry`);
updatePanel();
return false;
}
S.shieldBreakAttempts++;
const ok = sendTargetedPowerPacket(
portalItem,
Number(target.x) | 0,
Number(target.y) | 0,
null,
`${reason}-player-${targetId}-shield`
);
if (!ok) {
S.shieldBreakLastBlock = {
reason: 'shield-break-targeted-send-failed',
targetId,
cellId: Number(target.id) || 0,
t: now()
};
scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-send-retry`);
updatePanel();
return false;
}
recordManualItemSend(portalItem);
S.shieldBreakPortalSent = true;
S.shieldBreakSends++;
S.shieldBreakLastBlock = null;
S.shieldBreakLastSend = {
targetId,
cellId: Number(target.id) || 0,
x: Number(target.x) | 0,
y: Number(target.y) | 0,
size: Math.round(Number(target.size) || 0),
source: reason,
t: now()
};
S.lastDrop = {
item: portalItem,
x: Number(target.x) | 0,
y: Number(target.y) | 0,
reason: `${reason}-auto-break-shield`,
targetOwner: targetId,
targetCellId: Number(target.id) || 0,
t: now()
};
S.hotkeyMessage = `Break Shield ID ${targetId}: Portal @ (${Number(target.x) | 0}, ${Number(target.y) | 0})`;
updatePanel(true);
return true;
}
function processShieldBreakAnimation(id, animations, source = 'shield-break-animation') {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!targetId || Number(id) !== targetId) return;
if (S.shieldBreakTargetId !== targetId) {
resetShieldBreakCycle('shield-break-target-changed-packet');
S.shieldBreakTargetId = targetId;
}
const shieldMask = Number((S.PlayerFlags || {}).ANIMATION_SHIELD || 0);
const shieldOn = !!(Number(animations) & shieldMask);
const previousShieldOn = S.shieldBreakLastAnimations == null
? false
: !!(Number(S.shieldBreakLastAnimations) & shieldMask);
S.shieldBreakLastAnimations = Number(animations) || 0;
S.shieldBreakShieldOn = shieldOn;
if (!shieldOn) {
clearShieldBreakTimer();
S.shieldBreakPortalSent = false;
S.shieldBreakAttempts = 0;
S.shieldBreakDetectedAt = 0;
updatePanel();
return;
}
if (!previousShieldOn) {
S.shieldBreakPortalSent = false;
S.shieldBreakAttempts = 0;
S.shieldBreakDetectedAt = now();
S.shieldBreakLastBlock = null;
}
if (C.shieldBreakEnabled && !S.shieldBreakPortalSent && !S.shieldBreakTimer) {
// UPDATE_PLAYERS carries animation state, while the target cell's newest
// nx/ny is already kept by UPDATE_NODES. Send on the same event turn when
// possible; retry only if the live cell/cooldown/runtime is not ready yet.
attemptShieldBreakPortal(`${source}-shield-on`);
}
updatePanel();
}
function clearTargetFreezeTimer() {
if (S.targetFreezeTimer) {
clearTimeout(S.targetFreezeTimer);
S.targetFreezeTimer = null;
}
}
function clearTargetAntiRecTimer() {
if (S.targetAntiRecTimer) {
clearTimeout(S.targetAntiRecTimer);
S.targetAntiRecTimer = null;
}
}
function targetIdPlayer() {
const id = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!id) return null;
const players = pagePlayers();
return players && players[id] ? players[id] : null;
}
function targetIdAnimationFlags() {
const player = targetIdPlayer();
if (!player || !Number.isFinite(Number(player.animations))) return null;
return flagsFromAnimations(Number(player.animations));
}
function scheduleTargetFreezeCheck(delayMs = 0, reason = 'target-freeze-check') {
clearTargetFreezeTimer();
if (!C.targetAutoFreezeEnabled) return false;
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!targetId || targetId === Number(S.mainPlayerId)) return false;
S.targetFreezeTimer = setTimeout(() => {
S.targetFreezeTimer = null;
targetAutoFreezeTick(reason);
}, Math.ceil(Math.max(0, Number(delayMs) || 0)));
return true;
}
function targetAutoFreezeTick(source = 'target-freeze-tick', packetAnimations = null) {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
const E = S.EntityTypes || {};
const freezeItem = Number(E.FREEZE);
if (!C.targetAutoFreezeEnabled || !targetId || targetId === Number(S.mainPlayerId)) {
clearTargetFreezeTimer();
return false;
}
let state = null;
if (packetAnimations != null) {
state = flagsFromAnimations(Number(packetAnimations) || 0);
} else {
state = targetIdAnimationFlags();
}
// Fail closed until the target's real animation state is known. This avoids
// throwing Freeze at an Anti-Freeze target just because player data is stale.
if (!state) {
S.targetFreezeLastBlock = { reason: 'target-freeze-animation-unknown', targetId, t: now() };
scheduleTargetFreezeCheck(Math.max(50, Number(C.targetActionRetryMs) || 35), `${source}-state-retry`);
updatePanel();
return false;
}
if (state.frozen || state.antiFreeze) {
clearTargetFreezeTimer();
S.targetFreezeLastBlock = {
reason: state.antiFreeze ? 'target-has-antifreeze' : 'target-already-frozen',
targetId,
frozen: !!state.frozen,
antiFreeze: !!state.antiFreeze,
t: now()
};
updatePanel();
return false;
}
if (!S.mapsReady || !Number.isFinite(freezeItem)) {
S.targetFreezeLastBlock = { reason: 'target-freeze-map-not-ready', targetId, t: now() };
scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-map-retry`);
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.targetFreezeLastBlock = { reason: 'target-freeze-ws-not-ready', targetId, t: now() };
scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-ws-retry`);
return false;
}
if (!autoItemAvailable(freezeItem)) {
S.targetFreezeLastBlock = {
reason: 'target-freeze-no-item',
targetId,
inventory: knownInventoryCount(freezeItem),
t: now()
};
scheduleTargetFreezeCheck(Math.max(100, C.targetActionRetryMs), `${source}-inventory-retry`);
updatePanel();
return false;
}
const target = largestLiveCellForOwner(targetId);
if (!target) {
S.targetFreezeLastBlock = { reason: 'target-freeze-cell-not-ready', targetId, t: now() };
scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-cell-retry`);
updatePanel();
return false;
}
// Cooldown only prevents rejected duplicate packets; the DECISION to Freeze
// is still based on FROZEN / ANTI_FREEZE state, not a blind 2.9s interval.
const cooldownWait = manualItemWaitMs(freezeItem);
if (cooldownWait > 0) {
S.targetFreezeLastBlock = {
reason: 'target-freeze-item-cooldown',
targetId,
waitMs: cooldownWait,
t: now()
};
scheduleTargetFreezeCheck(cooldownWait + 1, `${source}-cooldown-recheck`);
updatePanel();
return false;
}
S.targetFreezeAttempts++;
const ok = sendTargetedPowerPacket(
freezeItem,
Number(target.x) | 0,
Number(target.y) | 0,
null,
`${source}-target-id-${targetId}`
);
if (!ok) {
S.targetFreezeLastBlock = { reason: 'target-freeze-send-failed', targetId, t: now() };
scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-send-retry`);
updatePanel();
return false;
}
recordManualItemSend(freezeItem);
S.targetFreezeSends++;
S.targetFreezeLastBlock = null;
S.targetFreezeLastSend = {
targetId,
cellId: Number(target.id) || 0,
x: Number(target.x) | 0,
y: Number(target.y) | 0,
size: Math.round(Number(target.size) || 0),
source,
t: now()
};
S.lastDrop = {
item: freezeItem,
x: Number(target.x) | 0,
y: Number(target.y) | 0,
reason: `${source}-auto-freeze-id`,
targetOwner: targetId,
targetCellId: Number(target.id) || 0,
t: now()
};
// Re-check after the server-provided Freeze cooldown. If the target has
// become Frozen or gained Anti-Freeze, this check stops immediately.
scheduleTargetFreezeCheck(
Math.max(1, manualItemCooldownMs(freezeItem) + 1),
`${source}-post-send-state-recheck`
);
updatePanel(true);
return true;
}
function processTargetIdAnimation(id, animations, source = 'target-id-animation') {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!targetId || Number(id) !== targetId) return;
const state = flagsFromAnimations(Number(animations) || 0);
if (C.targetAutoFreezeEnabled) {
if (state.frozen || state.antiFreeze) {
clearTargetFreezeTimer();
S.targetFreezeLastBlock = {
reason: state.antiFreeze ? 'target-has-antifreeze' : 'target-already-frozen',
targetId,
frozen: !!state.frozen,
antiFreeze: !!state.antiFreeze,
t: now()
};
} else {
// Run after the native client has had a chance to apply the same packet,
// while still using the packet's animation bits as the source of truth.
queueMicrotask(() => targetAutoFreezeTick(`${source}-effect-clear`, animations));
}
}
updatePanel();
}
function scheduleTargetAntiRec(delayMs = 0, reason = 'target-antirec-schedule') {
clearTargetAntiRecTimer();
if (!C.targetAutoAntiRecEnabled) return false;
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!targetId || targetId === Number(S.mainPlayerId)) return false;
S.targetAntiRecTimer = setTimeout(() => {
S.targetAntiRecTimer = null;
targetAutoAntiRecTick(reason);
}, Math.ceil(Math.max(0, Number(delayMs) || 0)));
return true;
}
function targetAutoAntiRecTick(source = 'target-antirec-tick') {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
const E = S.EntityTypes || {};
const antiRecItem = Number(E.ANTIRECOMBINE);
const baseInterval = Math.max(1, Number(C.targetAntiRecIntervalMs) || 460);
if (!C.targetAutoAntiRecEnabled || !targetId || targetId === Number(S.mainPlayerId)) {
clearTargetAntiRecTimer();
return false;
}
if (!S.mapsReady || !Number.isFinite(antiRecItem)) {
S.targetAntiRecLastBlock = { reason: 'target-antirec-map-not-ready', targetId, t: now() };
scheduleTargetAntiRec(Math.max(baseInterval, C.targetActionRetryMs), `${source}-map-retry`);
return false;
}
if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
S.targetAntiRecLastBlock = { reason: 'target-antirec-ws-not-ready', targetId, t: now() };
scheduleTargetAntiRec(Math.max(baseInterval, C.targetActionRetryMs), `${source}-ws-retry`);
return false;
}
if (!autoItemAvailable(antiRecItem)) {
S.targetAntiRecLastBlock = {
reason: 'target-antirec-no-item',
targetId,
inventory: knownInventoryCount(antiRecItem),
t: now()
};
scheduleTargetAntiRec(baseInterval, `${source}-inventory-retry`);
updatePanel();
return false;
}
const target = largestLiveCellForOwner(targetId);
if (!target) {
S.targetAntiRecLastBlock = { reason: 'target-antirec-cell-not-ready', targetId, t: now() };
scheduleTargetAntiRec(baseInterval, `${source}-cell-retry`);
updatePanel();
return false;
}
const cooldownWait = manualItemWaitMs(antiRecItem);
if (cooldownWait > 0) {
S.targetAntiRecLastBlock = {
reason: 'target-antirec-item-cooldown',
targetId,
waitMs: cooldownWait,
t: now()
};
scheduleTargetAntiRec(Math.max(1, cooldownWait + 1), `${source}-cooldown`);
updatePanel();
return false;
}
S.targetAntiRecAttempts++;
const ok = sendTargetedPowerPacket(
antiRecItem,
Number(target.x) | 0,
Number(target.y) | 0,
null,
`${source}-target-id-${targetId}`
);
if (ok) {
recordManualItemSend(antiRecItem);
S.targetAntiRecSends++;
S.targetAntiRecLastBlock = null;
S.targetAntiRecLastSend = {
targetId,
cellId: Number(target.id) || 0,
x: Number(target.x) | 0,
y: Number(target.y) | 0,
source,
t: now()
};
S.lastDrop = {
item: antiRecItem,
x: Number(target.x) | 0,
y: Number(target.y) | 0,
reason: `${source}-auto-antirec-id`,
targetOwner: targetId,
targetCellId: Number(target.id) || 0,
t: now()
};
} else {
S.targetAntiRecLastBlock = { reason: 'target-antirec-send-failed', targetId, t: now() };
}
scheduleTargetAntiRec(baseInterval, `${source}-next`);
updatePanel();
return ok;
}
function setTargetAutoFreeze(value) {
C.targetAutoFreezeEnabled = !!value;
clearTargetFreezeTimer();
savePrefs();
if (C.targetAutoFreezeEnabled) {
targetAutoFreezeTick('target-auto-freeze-enabled');
}
updatePanel(true);
return C.targetAutoFreezeEnabled;
}
function setTargetAutoAntiRec(value) {
C.targetAutoAntiRecEnabled = !!value;
clearTargetAntiRecTimer();
savePrefs();
if (C.targetAutoAntiRecEnabled) {
scheduleTargetAntiRec(0, 'target-auto-antirec-enabled');
}
updatePanel(true);
return C.targetAutoAntiRecEnabled;
}
function shieldBreakSelectablePlayers() {
const players = pagePlayers();
if (!players) return [];
const rows = [];
const seen = new Set();
for (const key of Object.keys(players)) {
const player = players[key];
if (!player) continue;
const id = normalizeShieldBreakPlayerId(
Number.isFinite(Number(player.id)) ? Number(player.id) : Number(key)
);
if (!id || seen.has(id)) continue;
seen.add(id);
const username = typeof player.username === 'string'
? player.username.trim()
: '';
rows.push({
id,
username,
isSelf: id === Number(S.mainPlayerId)
});
}
rows.sort((a, b) => a.id - b.id);
return rows;
}
function refreshShieldBreakPlayerSelect(select, force = false) {
if (!select) return;
const rows = shieldBreakSelectablePlayers();
const selectedId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
const signature = rows
.map(row => `${row.id}:${row.username}:${row.isSelf ? 1 : 0}`)
.join('|');
if (!force && signature === S.shieldBreakPlayerListSignature) {
const wanted = selectedId ? String(selectedId) : '';
if (select.value !== wanted && [...select.options].some(option => option.value === wanted)) {
select.value = wanted;
}
return;
}
S.shieldBreakPlayerListSignature = signature;
const fragment = document.createDocumentFragment();
const placeholder = document.createElement('option');
placeholder.value = '';
placeholder.textContent = rows.length
? `Select player… (${rows.length})`
: 'No players available';
fragment.appendChild(placeholder);
let selectedExists = false;
for (const row of rows) {
const option = document.createElement('option');
option.value = String(row.id);
option.textContent = `ID ${row.id}${row.username ? ` — ${row.username}` : ''}${row.isSelf ? ' (YOU)' : ''}`;
option.disabled = row.isSelf;
if (row.id === selectedId) selectedExists = true;
fragment.appendChild(option);
}
// Keep the saved selection visible across a short disconnect/reconnect,
// but mark it unavailable until that player appears again.
if (selectedId && !selectedExists) {
const missing = document.createElement('option');
missing.value = String(selectedId);
missing.textContent = `ID ${selectedId} — not currently available`;
missing.disabled = true;
fragment.appendChild(missing);
}
select.replaceChildren(fragment);
select.value = selectedId ? String(selectedId) : '';
}
function setShieldBreakPlayerId(value) {
const id = normalizeShieldBreakPlayerId(value);
C.shieldBreakPlayerId = id;
resetShieldBreakCycle('shield-break-player-id-set');
clearTargetFreezeTimer();
clearTargetAntiRecTimer();
S.targetFreezeLastBlock = null;
S.targetAntiRecLastBlock = null;
savePrefs();
if (id) {
const player = configuredShieldBreakPlayer();
if (player && Number.isFinite(Number(player.animations))) {
if (C.shieldBreakEnabled) {
processShieldBreakAnimation(id, Number(player.animations), 'shield-break-id-set-live-state');
}
if (C.targetAutoFreezeEnabled) {
processTargetIdAnimation(id, Number(player.animations), 'target-id-set-live-state');
}
}
if (C.targetAutoAntiRecEnabled) {
scheduleTargetAntiRec(0, 'target-id-set-antirec');
}
}
if (S.panel) {
refreshShieldBreakPlayerSelect(S.panel.querySelector('#acndBreakPlayerId'), true);
}
updatePanel(true);
return id;
}
function setShieldBreakEnabled(value) {
C.shieldBreakEnabled = !!value;
clearShieldBreakTimer();
savePrefs();
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
if (!C.shieldBreakEnabled) {
S.shieldBreakLastBlock = { reason: 'shield-break-disabled', t: now() };
updatePanel(true);
return false;
}
if (targetId) {
const player = configuredShieldBreakPlayer();
if (player && Number.isFinite(Number(player.animations))) {
processShieldBreakAnimation(targetId, Number(player.animations), 'shield-break-enabled-live-state');
}
}
updatePanel(true);
return true;
}
function processAnimationState(animations, source = 'unknown', meta = {}) {
if (animations == null) return;
const current = flagsFromAnimations(animations);
const previous = S.lastAnimations == null
? null
: flagsFromAnimations(S.lastAnimations);
S.lastAnimations = current.animations;
const transitionAtPerf = Number.isFinite(Number(meta.packetAt))
? Number(meta.packetAt)
: performance.now();
if (current.shield) {
cancelTimer('shieldPending');
S.shieldMissingSince = 0;
S.shieldAttempts = 0;
S.lastShieldWatchdogReason = 'shield-effect-confirmed';
if (!previous || !previous.shield) {
recordShieldOn(transitionAtPerf, `${source}-shield-on`);
}
}
if (!previous) {
S.shieldCycle++;
S.shieldAttempts = 0;
if (C.autoShield && !current.shield && fastOwnCell()) {
if (!S.shieldTiming.pendingCycle) {
beginShieldOffCycle(transitionAtPerf, `${source}-initial-no-shield`);
}
if (meta.packetDirect) {
fireShieldPacketDirect(
`${source}-initial-no-shield`,
Number(meta.packetAt) || now()
);
} else {
scheduleShield(`${source}-initial-no-shield`);
}
}
if (current.frozen && !current.antiFreeze) {
S.freezeCycle++;
S.antiFreezeAttempts = 0;
if (meta.packetDirect && C.antiFreezePacketDirectEnabled) {
fireAntiFreezePacketDirect(
`${source}-initial-frozen`,
transitionAtPerf
);
} else {
scheduleAntiFreeze(`${source}-initial-frozen`);
}
}
if (meta.packetDirect) queueMicrotask(updatePanel);
else updatePanel();
return;
}
if (previous.shield && !current.shield) {
beginShieldOffCycle(transitionAtPerf, `${source}-shield-off`);
S.shieldCycle++;
S.shieldAttempts = 0;
// Critical fast path: trust the animation bits parsed from the incoming
// UPDATE_PLAYERS packet. Do not reread window.players here because the
// game's own onmessage handler may not have applied the new state yet.
if (meta.packetDirect) {
fireShieldPacketDirect(
`${source}-shield-cleared`,
Number(meta.packetAt) || now()
);
} else {
scheduleShield(`${source}-shield-cleared`);
}
} else if (!current.shield && C.autoShield && S.lastOwnCount === 0 && fastOwnCell()) {
S.shieldCycle++;
S.shieldAttempts = 0;
scheduleShield(`${source}-spawn-without-shield`);
}
if (!previous.frozen && current.frozen) {
S.freezeCycle++;
S.antiFreezeAttempts = 0;
if (!current.antiFreeze) {
if (meta.packetDirect && C.antiFreezePacketDirectEnabled) {
fireAntiFreezePacketDirect(
`${source}-freeze-started`,
transitionAtPerf
);
} else {
scheduleAntiFreeze(`${source}-freeze-started`);
}
}
}
if (previous.frozen && !current.frozen) {
S.freezeCycle++;
S.antiFreezeAttempts = 0;
cancelTimer('antiFreezePending');
}
if (meta.packetDirect) queueMicrotask(updatePanel);
else updatePanel();
}
function shieldWatchdog(state, ownCount, source = 'poll') {
if (
!C.shieldWatchdogEnabled ||
!C.autoShield ||
!S.mapsReady ||
!S.mainPlayerId ||
!S.ws ||
S.ws.readyState !== 1 ||
ownCount <= 0
) {
S.shieldMissingSince = 0;
return false;
}
const shieldItem = S.EntityTypes && S.EntityTypes.SHIELD;
if (
Number.isFinite(Number(shieldItem)) &&
!autoItemAvailable(shieldItem)
) {
// The last accepted item may already be consumed while the Shield
// animation update is still in flight. Keep the verifier alive.
S.lastShieldWatchdogReason = 'shield-inventory-empty-waiting-effect';
if (!S.shieldPending) {
queueShieldVerification(
S.shieldCycle,
`${source}-inventory-empty-wait-effect`,
Math.max(35, Number(C.shieldRetryCheckMs) || 20)
);
}
return false;
}
const t = now();
if (state.shield) {
S.shieldMissingSince = 0;
S.shieldAttempts = 0;
return false;
}
if (!S.shieldMissingSince) S.shieldMissingSince = t;
// A timer handle can remain non-null after browser throttling or an
// interrupted callback. Remove it if it has clearly become stale.
if (
S.shieldPending &&
S.shieldPendingCreatedAt &&
t - S.shieldPendingCreatedAt > C.shieldPendingStaleMs
) {
cancelTimer('shieldPending');
S.lastShieldWatchdogReason = 'cleared-stale-pending-timer';
}
if (t - S.shieldMissingSince < C.shieldWatchdogGraceMs) return false;
// The dedicated retry timer normally owns recovery. The watchdog only
// restarts it if the timer disappeared or became stale while Shield is OFF.
if (S.shieldPending) return false;
if (t - S.shieldLastWatchdogKickAt < C.shieldWatchdogRetryGapMs) return false;
S.shieldLastWatchdogKickAt = t;
S.shieldWatchdogRecoveries++;
S.lastShieldWatchdogReason = `${source}-restart-until-effect-loop`;
return queueShieldVerification(
S.shieldCycle,
`${source}-watchdog-until-effect`,
1
);
}
function resetDefenseState(reason = 'reset') {
cancelTimer('shieldPending');
cancelTimer('antiFreezePending');
clearAntiRecLoopTimer();
clearAutoRecTimer();
clearShieldBreakTimer();
clearTargetFreezeTimer();
clearTargetAntiRecTimer();
S.shieldBreakTargetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
S.shieldBreakLastAnimations = null;
S.shieldBreakShieldOn = false;
S.shieldBreakPortalSent = false;
S.shieldBreakAttempts = 0;
S.shieldBreakDetectedAt = 0;
S.lastAnimations = null;
S.lastOwnCount = 0;
S.fastOwnCell = null;
S.fastOwnCellAt = 0;
S.shieldTiming.pendingCycle = null;
S.shieldTiming.sendsThisCycle = 0;
S.shieldCycle++;
S.shieldAttempts = 0;
S.shieldMissingSince = 0;
S.shieldLastWatchdogKickAt = 0;
S.lastShieldWatchdogReason = reason;
S.freezeCycle++;
S.antiFreezeAttempts = 0;
S.lastAntiFreezeReactionMs = null;
S.lastAntiFreezeReactionUs = null;
S.viewMotion.clear();
S.viewGlobalLocks.clear();
S.viewTargetLocks.clear();
S.lastViewScanAt = 0;
if (C.autoRecEnabled && S.ws && S.ws.readyState === 1) {
scheduleAutoRecLoop(1, `${reason}-auto-rec-rearm`);
}
if (S.ws && S.ws.readyState === 1) {
if (C.targetAutoFreezeEnabled && normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
scheduleTargetFreezeCheck(0, `${reason}-target-freeze-rearm`);
}
if (C.targetAutoAntiRecEnabled && normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
scheduleTargetAntiRec(0, `${reason}-target-antirec-rearm`);
}
}
}
function scheduleClientSync(source = 'ws', requestedDelayMs = 0) {
S.syncRequests++;
S.syncPendingSource = source;
if (S.syncTimer) return false;
const elapsed = now() - Number(S.syncLastAt || 0);
const configuredGap = Number(C.wsSyncMinMs);
const minimumGap = Math.max(
0,
Number.isFinite(configuredGap) ? configuredGap : 0
);
const waitMs = Math.max(
0,
Number(requestedDelayMs) || 0,
minimumGap - elapsed
);
S.syncTimer = setTimeout(() => {
S.syncTimer = null;
S.syncLastAt = now();
S.syncRuns++;
const pendingSource = S.syncPendingSource || source;
S.syncPendingSource = '';
syncFromClient(pendingSource);
}, Math.ceil(waitMs));
return true;
}
function syncFromClient(source = 'poll') {
if (!S.mapsReady || !S.mainPlayerId) {
updatePanel();
return;
}
const cells = ownCells();
const ownCount = cells.length;
const animations = liveAnimations();
const currentState = flagsFromAnimations(animations || 0);
if (animations != null) processAnimationState(animations, source);
// Independent liveness path. It does not depend on seeing ON -> OFF.
shieldWatchdog(currentState, ownCount, source);
// main_out.js has applied players/cells by this point.
viewDefenseTick(source, false);
// Target-ID Freeze is state-driven. Sync is only a liveness fallback; it
// checks the target's current effect bits before deciding whether to send.
if (C.targetAutoFreezeEnabled && normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
const targetFlags = targetIdAnimationFlags();
if (targetFlags && (targetFlags.frozen || targetFlags.antiFreeze)) {
clearTargetFreezeTimer();
} else if (!S.targetFreezeTimer) {
scheduleTargetFreezeCheck(0, `${source}-target-freeze-sync`);
}
}
if (S.lastOwnCount === 0 && ownCount > 0) {
const state = flagsFromAnimations(animations || 0);
S.shieldCycle++;
S.shieldAttempts = 0;
if (C.autoShield && !state.shield) {
scheduleShield(`${source}-own-cell-appeared`);
}
if (C.autoAntiFreeze && state.frozen && !state.antiFreeze) {
S.freezeCycle++;
S.antiFreezeAttempts = 0;
scheduleAntiFreeze(`${source}-own-cell-appeared-frozen`);
}
}
if (ownCount === 0) {
cancelTimer('shieldPending');
cancelTimer('antiFreezePending');
S.lastAnimations = null;
}
S.lastOwnCount = ownCount;
updatePanel();
}
function forceRescan() {
syncFromClient('manual-rescan');
return getState();
}
function setAutoShield(value) {
C.autoShield = !!value;
savePrefs();
if (!C.autoShield) {
cancelTimer('shieldPending');
} else {
S.shieldCycle++;
S.shieldAttempts = 0;
S.shieldMissingSince = 0;
S.shieldLastWatchdogKickAt = 0;
const state = flagsFromAnimations(liveAnimations() || 0);
if (!state.shield && strictLiveOwnDropCell()) scheduleShield('toggle-on');
}
updatePanel();
return C.autoShield;
}
function setAutoAntiFreeze(value) {
C.autoAntiFreeze = !!value;
savePrefs();
if (!C.autoAntiFreeze) {
cancelTimer('antiFreezePending');
} else {
S.freezeCycle++;
S.antiFreezeAttempts = 0;
const state = flagsFromAnimations(liveAnimations() || 0);
if (state.frozen && !state.antiFreeze) scheduleAntiFreeze('toggle-on');
}
updatePanel();
return C.autoAntiFreeze;
}
function getState() {
const animations = liveAnimations();
const effects = animations == null ? null : flagsFromAnimations(animations);
const cells = ownCells();
return {
ws: {
ready: !!(S.ws && S.ws.readyState === 1),
readyState: S.ws ? S.ws.readyState : -1,
url: S.ws ? S.ws.url : '',
socketsSeen: S.socketsSeen,
earlyInstalled: S.early.installed,
earlyCaptures: S.early.captures,
earlyErrors: [...S.early.errors]
},
nativeMaps: {
ready: S.mapsReady,
error: S.mapsError,
PacketIds: S.PacketIds,
EntityTypes: S.EntityTypes,
PlayerFlags: S.PlayerFlags
},
mainPlayerId: S.mainPlayerId,
ownCells: cells.map(c => ({
id: c.id,
x: Math.round(c.x),
y: Math.round(c.y),
size: Math.round(c.size)
})),
effects,
autoShield: C.autoShield,
autoAntiFreeze: C.autoAntiFreeze,
shieldAttempts: S.shieldAttempts,
shieldUntilEffectMode: {
enabled: true,
stopCondition: 'ANIMATION_SHIELD confirmed by server',
timerActive: !!S.shieldPending,
attemptsThisOffCycle: S.shieldAttempts,
checkMs: C.shieldRetryCheckMs,
minimumSendGapMs: C.shieldRetryMinSendGapMs,
effectiveSendGapMs: shieldRetrySendGapMs(),
retryMax: 'unlimited-until-effect'
},
lastShieldReactionMs: S.lastShieldReactionMs,
lastShieldReactionUs: S.lastShieldReactionUs,
lastShieldClearPacketAt: S.lastShieldClearPacketAt,
shieldTiming: {
clearToSendMs: S.shieldTiming.lastClearToSendMs,
sendToOnMs: S.shieldTiming.lastSendToOnMs,
clearToOnMs: S.shieldTiming.lastClearToOnMs,
visibleDurationMs: S.shieldTiming.lastVisibleDurationMs,
unshieldedGapMs: S.shieldTiming.lastUnshieldedGapMs,
sendIntervalMs: S.shieldTiming.lastSendIntervalMs,
clearIntervalMs: S.shieldTiming.lastClearIntervalMs,
onIntervalMs: S.shieldTiming.lastOnIntervalMs,
sendsThisCycle: S.shieldTiming.sendsThisCycle,
cyclesCompleted: S.shieldTiming.cyclesCompleted,
lastSendReason: S.shieldTiming.lastSendReason,
lastOnAt: S.shieldTiming.lastOnAtWall,
lastOffAt: S.shieldTiming.lastOffAtWall,
lastSendAt: S.shieldTiming.lastSendAtWall,
pending: S.shieldTiming.pendingCycle
? {
offAt: S.shieldTiming.pendingCycle.offAtWall,
firstSendAt: S.shieldTiming.pendingCycle.firstSendAtWall,
sends: S.shieldTiming.pendingCycle.sends,
source: S.shieldTiming.pendingCycle.source
}
: null,
history: S.shieldTiming.history.slice()
},
fastOwnCellAgeMs: S.fastOwnCellAt ? now() - S.fastOwnCellAt : null,
selfLock: {
enabled: true,
failClosed: !!C.selfDropFailClosed,
blocks: S.selfDropBlocks,
lastVerifiedCell: S.lastVerifiedSelfCell
},
shieldWatchdog: {
enabled: !!C.shieldWatchdogEnabled,
missingForMs: S.shieldMissingSince ? now() - S.shieldMissingSince : 0,
recoveries: S.shieldWatchdogRecoveries,
lastKickAt: S.shieldLastWatchdogKickAt,
lastReason: S.lastShieldWatchdogReason,
pending: !!S.shieldPending,
pendingAgeMs: S.shieldPendingCreatedAt ? now() - S.shieldPendingCreatedAt : 0
},
antiFreezeAttempts: S.antiFreezeAttempts,
antiFreezeTiming: {
packetDirectEnabled: !!C.antiFreezePacketDirectEnabled,
reactionMs: S.lastAntiFreezeReactionMs,
reactionUs: S.lastAntiFreezeReactionUs,
frozenPacketAt: S.lastAntiFreezeFrozenPacketAt,
packetDirectSends: S.antiFreezePacketDirectSends,
packetDirectFallbacks: S.antiFreezePacketDirectFallbacks,
pendingRetry: !!S.antiFreezePending,
retryMs: C.antiFreezeRetryMs
},
hotkeys: {
shieldCode: C.shieldHotkeyCode,
shieldLabel: hotkeyLabel(C.shieldHotkeyCode),
fastShieldCode: C.fastShieldHotkeyCode,
fastShieldLabel: hotkeyLabel(C.fastShieldHotkeyCode),
pushCode: C.pushBurstHotkeyCode,
pushLabel: hotkeyLabel(C.pushBurstHotkeyCode),
growthCode: C.growthHotkeyCode,
growthLabel: hotkeyLabel(C.growthHotkeyCode),
comboCode: C.comboHotkeyCode,
comboLabel: hotkeyLabel(C.comboHotkeyCode),
capture: S.hotkeyCapture,
message: S.hotkeyMessage
},
fastShield: {
hotkeyCode: C.fastShieldHotkeyCode,
hotkeyLabel: hotkeyLabel(C.fastShieldHotkeyCode),
intervalMs: C.fastShieldIntervalMs,
running: !!S.fastShieldRunning,
attempts: S.fastShieldAttempts,
sends: S.fastShieldSends,
last: S.lastFastShield,
target: 'largest-live-own-cell'
},
pushBurst: {
enabled: !!C.pushBurstEnabled,
hotkeyCode: C.pushBurstHotkeyCode,
hotkeyLabel: hotkeyLabel(C.pushBurstHotkeyCode),
count: C.pushBurstCount,
intervalMs: C.pushBurstIntervalMs,
running: S.pushBurstRunning,
bursts: S.pushBursts,
packetsSent: S.pushPacketsSent,
last: S.lastPushBurst
},
targetIdAutomation: {
playerId: normalizeShieldBreakPlayerId(C.shieldBreakPlayerId),
autoFreezeEnabled: !!C.targetAutoFreezeEnabled,
autoAntiRecEnabled: !!C.targetAutoAntiRecEnabled,
freezeSends: S.targetFreezeSends,
freezeAttempts: S.targetFreezeAttempts,
freezeLastSend: S.targetFreezeLastSend,
freezeLastBlock: S.targetFreezeLastBlock,
antiRecSends: S.targetAntiRecSends,
antiRecAttempts: S.targetAntiRecAttempts,
antiRecLastSend: S.targetAntiRecLastSend,
antiRecLastBlock: S.targetAntiRecLastBlock
},
shieldBreaker: {
enabled: !!C.shieldBreakEnabled,
playerId: normalizeShieldBreakPlayerId(C.shieldBreakPlayerId),
shieldOn: !!S.shieldBreakShieldOn,
portalSentThisShield: !!S.shieldBreakPortalSent,
timerActive: !!S.shieldBreakTimer,
attempts: S.shieldBreakAttempts,
sends: S.shieldBreakSends,
detectedAt: S.shieldBreakDetectedAt,
lastSend: S.shieldBreakLastSend,
lastBlock: S.shieldBreakLastBlock
},
manualTargeting: {
mouse: S.mouseWorld
? {
x: S.mouseWorld.x,
y: S.mouseWorld.y,
ageMs: now() - S.mouseWorld.t,
source: S.mouseWorld.source
}
: null,
outgoingPacketsObserved: S.outgoingPacketsObserved,
growth: {
mode: 'growth30-plus-shield-no-mouse',
amount: 30,
hotkeyCode: C.growthHotkeyCode,
hotkeyLabel: hotkeyLabel(C.growthHotkeyCode),
sends: S.growthSends,
last: S.lastGrowthSend
},
autoPellet: {
enabled: !!C.autoPelletEnabled,
amount: C.autoPelletAmount,
intervalMs: C.autoPelletIntervalMs,
worldTarget: configuredAutoPelletWorld(),
runs: S.autoPelletRuns,
packets: S.autoPelletPackets,
totalPellets: S.autoPelletTotal,
last: S.lastAutoPellet
},
combo: {
running: S.comboRunning,
queueLength: S.comboQueue.length,
inventory: comboInventoryStatus(),
currentPortalInventory: portalInventoryCount(),
livePortalNodes: livePortalNodeIds().size,
hotkeyCode: C.comboHotkeyCode,
hotkeyLabel: hotkeyLabel(C.comboHotkeyCode),
sends: S.comboSends,
blocks: S.comboBlocks,
lastSend: S.lastComboSend,
lastBlock: S.lastComboBlock
}
},
viewDefense: {
mode: C.viewTargetMode,
centerDropOnly: !!C.viewCenterDropOnly,
autoAntiRec: !!C.autoViewAntiRec,
autoFreeze: !!C.autoViewFreeze,
freezeMode: 'mouse-hover-center-skip-antifreeze',
scanMs: C.viewScanMs,
forwardDrop: {
enabled: !!C.viewForwardDropEnabled,
base: C.viewForwardDropBase,
sizeFactor: C.viewForwardDropSizeFactor,
speedFactor: C.viewForwardDropSpeedFactor,
min: C.viewForwardDropMin,
max: C.viewForwardDropMax
},
scans: S.viewScans,
drops: S.viewDrops,
blocks: S.viewBlocks,
antiRecCooldownMs: S.EntityTypes
? viewItemIntervalMs(S.EntityTypes.ANTIRECOMBINE)
: null,
antiRecMode: 'mouse-hover-single-target',
antiRecPerPlayerLockMs: C.viewAntiRecPerPlayerLockMs,
antiRecContinuous: {
enabled: !!C.autoViewAntiRec,
timerActive: !!S.antiRecLoopTimer,
nextDueAt: S.antiRecLoopDueAt,
runs: S.antiRecLoopRuns,
totalDrops: S.antiRecLoopDrops,
lastRunAt: S.antiRecLoopLastRunAt,
lastSource: S.antiRecLoopLastSource,
lastVisibleCount: S.antiRecLoopLastVisibleCount,
lastSentOwners: S.antiRecLoopLastSentOwners.slice()
},
antiRecEffectiveTargetLockMs: S.EntityTypes
? viewTargetLockMs(S.EntityTypes.ANTIRECOMBINE)
: C.viewAntiRecPerPlayerLockMs,
visiblePlayerCount: S.visibleEnemyCount,
visiblePlayerIds: S.visibleEnemyOwnerIds.slice(),
visiblePlayerUpdatedAt: S.visibleEnemyUpdatedAt,
freezeCooldownMs: S.EntityTypes
? viewItemIntervalMs(S.EntityTypes.FREEZE)
: C.viewFreezePerPlayerLockMs,
freezePerPlayerLockMs: C.viewFreezePerPlayerLockMs,
mouseTargetExtraRadius: C.viewMouseTargetExtraRadius,
lastTarget: S.lastViewTarget,
lastDrop: S.lastViewDrop,
lastBlock: S.lastViewBlock
},
performance: {
mode: 'event-driven-low-lag',
statePollMs: C.statePollMs,
pollEnabled: !!S.poll,
eventDrivenOnly: Number(C.statePollMs) === 0,
wsSyncMinMs: C.wsSyncMinMs,
viewScanMs: C.viewScanMs,
uiUpdateMinMs: C.uiUpdateMinMs,
syncRequests: S.syncRequests,
syncRuns: S.syncRuns,
syncPending: !!S.syncTimer,
uiRequests: S.uiRequests,
uiUpdates: S.uiUpdates,
uiPending: !!S.uiUpdateTimer
},
lastDrop: S.lastDrop,
lastBlock: S.lastBlock,
messages: S.messages,
lastPacketOp: S.lastPacketOp
};
}
function createPanel() {
if (!C.panel || document.getElementById('acNativeDefenseV31')) return;
const style = document.createElement('style');
style.textContent = `
#acNativeDefenseV31 {
position: fixed;
left: 14px;
top: 100px;
z-index: 2147483647;
width: 292px;
color: #eaffff;
background: rgba(4, 12, 17, .92);
border: 1px solid rgba(67, 243, 255, .65);
border-radius: 9px;
padding: 8px;
font: 12px/1.25 Arial, sans-serif;
box-shadow: 0 0 18px rgba(0, 229, 255, .18);
user-select: none;
}
#acNativeDefenseV31 .head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
font-weight: 700;
}
#acNativeDefenseV31.minimized {
width: auto;
min-width: 205px;
}
#acNativeDefenseV31.minimized .head {
margin-bottom: 0;
}
#acNativeDefenseV31.minimized > :not(.head) {
display: none !important;
}
#acNativeDefenseV31 .head-actions {
display: flex;
align-items: center;
gap: 5px;
}
#acNativeDefenseV31 .mini-btn {
min-width: 28px;
padding: 2px 6px;
font-size: 13px;
line-height: 1.1;
}
#acNativeDefenseV31 .status {
font-size: 11px;
opacity: .9;
}
#acNativeDefenseV31 label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin: 5px 0;
}
#acNativeDefenseV31 button {
border: 1px solid rgba(67, 243, 255, .5);
background: rgba(18, 56, 67, .9);
color: #eaffff;
border-radius: 6px;
padding: 4px 7px;
cursor: pointer;
}
#acNativeDefenseV31 .keybtn {
min-width: 54px;
font-weight: 700;
color: #8ffcff;
}
#acNativeDefenseV31 .keybtn.capture {
color: #111;
background: #ffe16b;
border-color: #fff0a6;
}
#acNativeDefenseV31 .hotkey-msg {
min-height: 14px;
margin-top: 4px;
font-size: 10px;
color: #8ffcff;
}
#acNativeDefenseV31 .power-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: 5px;
align-items: center;
margin-top: 6px;
}
#acNativeDefenseV31 .power-row button:first-child {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#acNativeDefenseV31 .target-box {
margin-top: 7px;
padding: 6px;
border: 1px solid rgba(255, 188, 67, .42);
border-radius: 7px;
background: rgba(66, 39, 5, .22);
}
#acNativeDefenseV31 .target-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 6px;
align-items: center;
margin-top: 5px;
}
#acNativeDefenseV31 .target-input {
min-width: 0;
width: 100%;
box-sizing: border-box;
border: 1px solid rgba(255, 188, 67, .55);
border-radius: 5px;
color: #fff6dc;
background: rgba(20, 14, 6, .96);
padding: 5px 6px;
}
#acNativeDefenseV31 .target-state {
max-width: 128px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 10px;
}
#acNativeDefenseV31 .amount {
width: 52px;
height: 25px;
box-sizing: border-box;
border: 1px solid rgba(67, 243, 255, .45);
border-radius: 5px;
color: #eaffff;
background: rgba(6, 22, 29, .95);
text-align: center;
}
#acNativeDefenseV31 .ok { color: #63ff98; }
#acNativeDefenseV31 .bad { color: #ff6b77; }
#acNativeDefenseV31 .muted { opacity: .7; }
`;
document.documentElement.appendChild(style);
const panel = document.createElement('div');
panel.id = 'acNativeDefenseV31';
panel.innerHTML = `
<div class="head">
<span>AC Shield Until Effect v39.0.0</span>
<span class="head-actions">
<span id="acndWs">WS×</span>
<button id="acndMini" class="mini-btn" type="button" title="Minimize">−</button>
</span>
</div>
<div class="status" id="acndStatus">Loading native maps…</div>
<label>
<span>Auto Shield</span>
<span style="display:flex;align-items:center;gap:5px">
<button id="acndShieldKey" class="keybtn" type="button">${hotkeyLabel(C.shieldHotkeyCode)}</button>
<input id="acndShield" type="checkbox">
</span>
</label>
<label>
<span>Auto Anti-Freeze</span>
<input id="acndAf" type="checkbox">
</label>
<label>
<span>Anti-Rec Mouse (460ms)</span>
<input id="acndArView" type="checkbox">
</label>
<label>
<span>Auto Rec [E] (1ms)</span>
<input id="acndAutoRec" type="checkbox">
</label>
<label>
<span>Freeze Mouse (2.9s, skip AF)</span>
<input id="acndFreezeView" type="checkbox">
</label>
<div class="target-box">
<div style="font-weight:700;margin-bottom:4px">Target Player ID</div>
<div class="target-row">
<select id="acndBreakPlayerId" class="target-input" title="Select a player currently known by the client">
<option value="">Select player…</option>
</select>
<span id="acndBreakState" class="target-state muted">ID: —</span>
</div>
<label style="margin-top:5px">
<span>Break Shield → Portal</span>
<input id="acndBreakShield" type="checkbox">
</label>
<label>
<span>Auto Freeze ID (effect check)</span>
<input id="acndTargetFreeze" type="checkbox">
</label>
<label>
<span>Auto Anti-Rec ID (460ms)</span>
<input id="acndTargetAntiRec" type="checkbox">
</label>
</div>
<div style="display:flex;gap:5px;margin-top:7px;flex-wrap:wrap">
<button id="acndTestShield">Shield Now</button>
<button id="acndPush15">Push ×15</button>
<button id="acndPushKey" class="keybtn" type="button">${hotkeyLabel(C.pushBurstHotkeyCode)}</button>
<button id="acndRescan">Rescan</button>
</div>
<div class="power-row">
<button id="acndFastShield" type="button">Fast Shield @ MAIN: OFF</button>
<span id="acndFastShieldState">50ms</span>
<button id="acndFastShieldKey" class="keybtn" type="button">${hotkeyLabel(C.fastShieldHotkeyCode)}</button>
</div>
<div class="power-row">
<button id="acndGrowth">Growth ×30 + Shield</button>
<span>NO MOUSE</span>
<button id="acndGrowthKey" class="keybtn" type="button">${hotkeyLabel(C.growthHotkeyCode)}</button>
</div>
<div class="power-row">
<button id="acndAutoPellet" type="button">Auto Pellet ×30 / 5s: OFF</button>
<span id="acndPelletCount">Pellet: 0</span>
<span></span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr auto;gap:5px;margin-top:5px;align-items:center">
<input id="acndPelletWorldX" type="number" step="1" placeholder="World X" style="min-width:0;width:100%;box-sizing:border-box;background:#111;color:#eee;border:1px solid #555;border-radius:4px;padding:4px">
<input id="acndPelletWorldY" type="number" step="1" placeholder="World Y" style="min-width:0;width:100%;box-sizing:border-box;background:#111;color:#eee;border:1px solid #555;border-radius:4px;padding:4px">
<button id="acndPelletSetMouse" type="button">Set @ Mouse</button>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;gap:6px;margin-top:4px">
<span class="muted">Set Mouse hotkey</span>
<button id="acndPelletMouseKey" class="keybtn" type="button">${hotkeyLabel(C.autoPelletSetMouseHotkeyCode)}</button>
</div>
<div id="acndPelletWorldInfo" class="muted" style="margin-top:3px">World target: not set</div>
<div class="power-row">
<button id="acndCombo">SMART: Shield? P→AR→F→V : AR→F→V</button>
<span></span>
<button id="acndComboKey" class="keybtn" type="button">${hotkeyLabel(C.comboHotkeyCode)}</button>
</div>
<div id="acndHotkeyMsg" class="hotkey-msg"></div>
<div class="muted" id="acndDetail" style="margin-top:6px"></div>
`;
document.body.appendChild(panel);
S.panel = panel;
panel.querySelector('#acndShield').checked = C.autoShield;
panel.querySelector('#acndAf').checked = C.autoAntiFreeze;
panel.querySelector('#acndArView').checked = C.autoViewAntiRec;
panel.querySelector('#acndAutoRec').checked = C.autoRecEnabled;
panel.querySelector('#acndFreezeView').checked = C.autoViewFreeze;
panel.querySelector('#acndBreakShield').checked = C.shieldBreakEnabled;
panel.querySelector('#acndTargetFreeze').checked = C.targetAutoFreezeEnabled;
panel.querySelector('#acndTargetAntiRec').checked = C.targetAutoAntiRecEnabled;
refreshShieldBreakPlayerSelect(panel.querySelector('#acndBreakPlayerId'), true);
panel.classList.toggle('minimized', !!C.panelMinimized);
panel.querySelector('#acndMini').textContent = C.panelMinimized ? '+' : '−';
panel.querySelector('#acndMini').title = C.panelMinimized ? 'Expand' : 'Minimize';
panel.querySelector('#acndShield').onchange = e => setAutoShield(e.target.checked);
panel.querySelector('#acndAf').onchange = e => setAutoAntiFreeze(e.target.checked);
panel.querySelector('#acndArView').onchange = e => setAutoViewAntiRec(e.target.checked);
panel.querySelector('#acndAutoRec').onchange = e => setAutoRec(e.target.checked);
panel.querySelector('#acndFreezeView').onchange = e => setAutoViewFreeze(e.target.checked);
panel.querySelector('#acndBreakShield').onchange = e => setShieldBreakEnabled(e.target.checked);
panel.querySelector('#acndTargetFreeze').onchange = e => setTargetAutoFreeze(e.target.checked);
panel.querySelector('#acndTargetAntiRec').onchange = e => setTargetAutoAntiRec(e.target.checked);
const breakPlayerIdSelect = panel.querySelector('#acndBreakPlayerId');
breakPlayerIdSelect.onchange = () => setShieldBreakPlayerId(breakPlayerIdSelect.value);
breakPlayerIdSelect.onkeydown = event => event.stopPropagation();
breakPlayerIdSelect.onkeyup = event => event.stopPropagation();
panel.querySelector('#acndMini').onclick = () => {
C.panelMinimized = !C.panelMinimized;
panel.classList.toggle('minimized', C.panelMinimized);
panel.querySelector('#acndMini').textContent = C.panelMinimized ? '+' : '−';
panel.querySelector('#acndMini').title = C.panelMinimized ? 'Expand' : 'Minimize';
savePrefs();
};
panel.querySelector('#acndTestShield').onclick = () => sendShield('manual-panel-shield');
panel.querySelector('#acndPush15').onclick = () => sendPushBurst(C.pushBurstCount, 'panel-push15');
panel.querySelector('#acndShieldKey').onclick = () => beginHotkeyCapture('shield');
panel.querySelector('#acndFastShieldKey').onclick = () => beginHotkeyCapture('fastShield');
panel.querySelector('#acndFastShield').onclick = event => {
event.preventDefault();
const enabled = toggleFastShieldDrop('panel-toggle');
S.hotkeyMessage = `Fast Shield ${enabled ? 'ON' : 'OFF'} · MAIN · ${C.fastShieldIntervalMs}ms`;
updatePanel(true);
};
panel.querySelector('#acndPushKey').onclick = () => beginHotkeyCapture('push');
panel.querySelector('#acndGrowth').onclick = () =>
sendGrowth30Shield('panel-growth30-shield');
panel.querySelector('#acndAutoPellet').onclick = () =>
setAutoPellet(!C.autoPelletEnabled);
const pelletWorldXInput = panel.querySelector('#acndPelletWorldX');
const pelletWorldYInput = panel.querySelector('#acndPelletWorldY');
const applyPelletWorldInputs = () => {
if (!pelletWorldXInput.value.trim() || !pelletWorldYInput.value.trim()) return;
setAutoPelletWorld(Number(pelletWorldXInput.value), Number(pelletWorldYInput.value), 'panel-world-input');
};
pelletWorldXInput.onchange = applyPelletWorldInputs;
pelletWorldYInput.onchange = applyPelletWorldInputs;
panel.querySelector('#acndPelletSetMouse').onclick = () =>
captureAutoPelletWorldFromMouse('panel-set-at-mouse');
panel.querySelector('#acndPelletMouseKey').onclick = () =>
beginHotkeyCapture('pelletMouse');
panel.querySelector('#acndPelletMouseKey').oncontextmenu = event => {
event.preventDefault();
clearHotkey('pelletMouse');
};
panel.querySelector('#acndCombo').onclick = () =>
sendTargetedCombo('panel-smart-drop');
panel.querySelector('#acndGrowthKey').onclick = () =>
beginHotkeyCapture('growth');
panel.querySelector('#acndComboKey').onclick = () =>
beginHotkeyCapture('combo');
panel.querySelector('#acndShieldKey').oncontextmenu = event => {
event.preventDefault();
clearHotkey('shield');
};
panel.querySelector('#acndFastShieldKey').oncontextmenu = event => {
event.preventDefault();
stopFastShieldDrop('hotkey-cleared');
clearHotkey('fastShield');
};
panel.querySelector('#acndPushKey').oncontextmenu = event => {
event.preventDefault();
clearHotkey('push');
};
panel.querySelector('#acndGrowthKey').oncontextmenu = event => {
event.preventDefault();
clearHotkey('growth');
};
panel.querySelector('#acndComboKey').oncontextmenu = event => {
event.preventDefault();
clearHotkey('combo');
};
panel.querySelector('#acndRescan').onclick = forceRescan;
updatePanel();
}
function updatePanel(force = false) {
const panel = S.panel;
if (!panel) return;
S.uiRequests++;
const t = now();
const minimumGap = Math.max(0, Number(C.uiUpdateMinMs) || 140);
const elapsed = t - Number(S.uiLastUpdateAt || 0);
if (!force && elapsed < minimumGap) {
if (!S.uiUpdateTimer) {
S.uiUpdateTimer = setTimeout(() => {
S.uiUpdateTimer = null;
updatePanel(true);
}, Math.ceil(minimumGap - elapsed));
}
return;
}
if (force && S.uiUpdateTimer) {
clearTimeout(S.uiUpdateTimer);
S.uiUpdateTimer = null;
}
S.uiLastUpdateAt = t;
S.uiUpdates++;
const ws = panel.querySelector('#acndWs');
const status = panel.querySelector('#acndStatus');
const detail = panel.querySelector('#acndDetail');
const shieldCheckbox = panel.querySelector('#acndShield');
const autoRecCheckbox = panel.querySelector('#acndAutoRec');
const breakShieldCheckbox = panel.querySelector('#acndBreakShield');
const targetFreezeCheckbox = panel.querySelector('#acndTargetFreeze');
const targetAntiRecCheckbox = panel.querySelector('#acndTargetAntiRec');
const breakPlayerIdSelect = panel.querySelector('#acndBreakPlayerId');
const breakState = panel.querySelector('#acndBreakState');
const shieldKeyButton = panel.querySelector('#acndShieldKey');
const fastShieldButton = panel.querySelector('#acndFastShield');
const fastShieldState = panel.querySelector('#acndFastShieldState');
const fastShieldKeyButton = panel.querySelector('#acndFastShieldKey');
const pushKeyButton = panel.querySelector('#acndPushKey');
const growthKeyButton = panel.querySelector('#acndGrowthKey');
const comboKeyButton = panel.querySelector('#acndComboKey');
const growthButton = panel.querySelector('#acndGrowth');
const autoPelletButton = panel.querySelector('#acndAutoPellet');
const pelletCount = panel.querySelector('#acndPelletCount');
const pelletWorldXInput = panel.querySelector('#acndPelletWorldX');
const pelletWorldYInput = panel.querySelector('#acndPelletWorldY');
const pelletWorldInfo = panel.querySelector('#acndPelletWorldInfo');
const pelletMouseKeyButton = panel.querySelector('#acndPelletMouseKey');
const hotkeyMsg = panel.querySelector('#acndHotkeyMsg');
if (shieldCheckbox && shieldCheckbox.checked !== !!C.autoShield) {
shieldCheckbox.checked = !!C.autoShield;
}
if (autoRecCheckbox && autoRecCheckbox.checked !== !!C.autoRecEnabled) {
autoRecCheckbox.checked = !!C.autoRecEnabled;
}
if (breakShieldCheckbox && breakShieldCheckbox.checked !== !!C.shieldBreakEnabled) {
breakShieldCheckbox.checked = !!C.shieldBreakEnabled;
}
if (targetFreezeCheckbox && targetFreezeCheckbox.checked !== !!C.targetAutoFreezeEnabled) {
targetFreezeCheckbox.checked = !!C.targetAutoFreezeEnabled;
}
if (targetAntiRecCheckbox && targetAntiRecCheckbox.checked !== !!C.targetAutoAntiRecEnabled) {
targetAntiRecCheckbox.checked = !!C.targetAutoAntiRecEnabled;
}
if (breakPlayerIdSelect) {
refreshShieldBreakPlayerSelect(breakPlayerIdSelect);
}
if (breakState) {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
const targetPlayer = targetId ? configuredShieldBreakPlayer() : null;
const liveAnim = targetPlayer && Number.isFinite(Number(targetPlayer.animations))
? flagsFromAnimations(Number(targetPlayer.animations))
: null;
const shieldOn = !!(S.shieldBreakShieldOn || (liveAnim && liveAnim.shield));
if (!targetId) {
breakState.textContent = 'ID: —';
breakState.className = 'target-state muted';
} else if (targetId === Number(S.mainPlayerId)) {
breakState.textContent = `ID ${targetId}: SELF`;
breakState.className = 'target-state bad';
} else if (!targetPlayer) {
breakState.textContent = `ID ${targetId}: not seen`;
breakState.className = 'target-state muted';
} else if (liveAnim && liveAnim.antiFreeze) {
breakState.textContent = `ID ${targetId}: ANTI-FREEZE`;
breakState.className = 'target-state bad';
} else if (liveAnim && liveAnim.frozen) {
breakState.textContent = `ID ${targetId}: FROZEN`;
breakState.className = 'target-state ok';
} else if (S.shieldBreakPortalSent && shieldOn) {
breakState.textContent = `ID ${targetId}: PORTAL ✓`;
breakState.className = 'target-state ok';
} else if (shieldOn) {
breakState.textContent = `ID ${targetId}: SHIELD`;
breakState.className = 'target-state bad';
} else {
breakState.textContent = `ID ${targetId}: FREEZE READY`;
breakState.className = 'target-state ok';
}
const targetBits = liveAnim
? `shield=${liveAnim.shield ? '1' : '0'} frozen=${liveAnim.frozen ? '1' : '0'} af=${liveAnim.antiFreeze ? '1' : '0'}`
: 'animation=unknown';
breakState.title = `${targetBits} | Freeze sends: ${S.targetFreezeSends} | Anti-Rec sends: ${S.targetAntiRecSends}`;
}
if (shieldKeyButton) {
shieldKeyButton.textContent = S.hotkeyCapture === 'shield'
? 'PRESS…'
: hotkeyLabel(C.shieldHotkeyCode);
shieldKeyButton.classList.toggle('capture', S.hotkeyCapture === 'shield');
shieldKeyButton.title = 'Click to set Auto Shield toggle key; right-click to clear';
}
if (fastShieldKeyButton) {
fastShieldKeyButton.textContent = S.hotkeyCapture === 'fastShield'
? 'PRESS…'
: hotkeyLabel(C.fastShieldHotkeyCode);
fastShieldKeyButton.classList.toggle('capture', S.hotkeyCapture === 'fastShield');
fastShieldKeyButton.title = 'Press once: ON; press again: OFF. Shield every 50ms at largest own cell; right-click to clear';
}
if (fastShieldButton) {
fastShieldButton.classList.toggle('active', !!S.fastShieldRunning);
fastShieldButton.textContent = S.fastShieldRunning
? 'Fast Shield @ MAIN: ON'
: 'Fast Shield @ MAIN: OFF';
fastShieldButton.title = 'Toggle continuous Shield every 50ms at your largest/main cell. Does not use real mouse target.';
}
if (fastShieldState) {
fastShieldState.textContent = `${C.fastShieldIntervalMs}ms · ${S.fastShieldSends}`;
fastShieldState.title = `Attempts: ${S.fastShieldAttempts} · Successful sends: ${S.fastShieldSends}`;
}
if (pushKeyButton) {
pushKeyButton.textContent = S.hotkeyCapture === 'push'
? 'PRESS…'
: hotkeyLabel(C.pushBurstHotkeyCode);
pushKeyButton.classList.toggle('capture', S.hotkeyCapture === 'push');
pushKeyButton.title = 'Click to set Push ×15 key; right-click to clear';
}
if (growthKeyButton) {
growthKeyButton.textContent = S.hotkeyCapture === 'growth'
? 'PRESS…'
: hotkeyLabel(C.growthHotkeyCode);
growthKeyButton.classList.toggle('capture', S.hotkeyCapture === 'growth');
growthKeyButton.title = 'Click to set Growth ×30 + Shield key; no mouse/aim; right-click to clear';
}
if (pelletMouseKeyButton) {
pelletMouseKeyButton.textContent = S.hotkeyCapture === 'pelletMouse'
? 'PRESS…'
: hotkeyLabel(C.autoPelletSetMouseHotkeyCode);
pelletMouseKeyButton.classList.toggle('capture', S.hotkeyCapture === 'pelletMouse');
pelletMouseKeyButton.title = 'Click to set Auto Pellet Set @ Mouse hotkey; right-click to clear';
}
if (comboKeyButton) {
comboKeyButton.textContent = S.hotkeyCapture === 'combo'
? 'PRESS…'
: hotkeyLabel(C.comboHotkeyCode);
comboKeyButton.classList.toggle('capture', S.hotkeyCapture === 'combo');
comboKeyButton.title = 'Click to set combo key; right-click to clear';
}
if (growthButton) {
growthButton.textContent = 'Growth ×30 + Shield';
growthButton.title = 'DROP_GROWTH(30) + DROP_SHIELD only. No mouse read, aim, move or restore.';
}
if (autoPelletButton) {
autoPelletButton.textContent = `Auto Pellet ×${C.autoPelletAmount} / ${(C.autoPelletIntervalMs / 1000).toFixed(0)}s: ${C.autoPelletEnabled ? 'ON' : 'OFF'}`;
autoPelletButton.classList.toggle('active', !!C.autoPelletEnabled);
const apWorldButton = configuredAutoPelletWorld();
autoPelletButton.title = apWorldButton
? `Fixed WORLD target (${apWorldButton.x}, ${apWorldButton.y})`
: 'Set WORLD X/Y or click Set @ Mouse';
}
const apWorld = configuredAutoPelletWorld();
if (pelletWorldXInput && document.activeElement !== pelletWorldXInput) pelletWorldXInput.value = apWorld ? String(apWorld.x) : '';
if (pelletWorldYInput && document.activeElement !== pelletWorldYInput) pelletWorldYInput.value = apWorld ? String(apWorld.y) : '';
if (pelletWorldInfo) pelletWorldInfo.textContent = apWorld
? `World target: X ${apWorld.x} · Y ${apWorld.y}`
: 'World target: not set';
if (pelletCount) {
pelletCount.textContent = `Pellet: ${S.autoPelletTotal}`;
pelletCount.title = `${S.autoPelletPackets} successful auto Growth packets`;
}
if (hotkeyMsg) {
hotkeyMsg.textContent = S.hotkeyMessage ||
`SH:${hotkeyLabel(C.shieldHotkeyCode)} · FSH:${hotkeyLabel(C.fastShieldHotkeyCode)}@50ms · P15:${hotkeyLabel(C.pushBurstHotkeyCode)} · G30+SH:${hotkeyLabel(C.growthHotkeyCode)} · AP-Mouse:${hotkeyLabel(C.autoPelletSetMouseHotkeyCode)} · Combo:${hotkeyLabel(C.comboHotkeyCode)}`;
}
const wsReady = !!(S.ws && S.ws.readyState === 1);
ws.textContent = wsReady ? 'WS' : 'WS×';
ws.className = wsReady ? 'ok' : 'bad';
const effects = flagsFromAnimations(liveAnimations() || 0);
const ownCount = ownCells().length;
if (!S.mapsReady) {
status.textContent = `MAP× ${S.mapsError || 'loading'}`;
status.className = 'status bad';
} else if (!S.mainPlayerId) {
status.textContent = 'Waiting INIT_PLAYER…';
status.className = 'status bad';
} else {
status.textContent = `ID ${S.mainPlayerId} · cells ${ownCount}`;
status.className = 'status ok';
}
detail.textContent =
`Shield:${effects.shield ? 'ON' : 'OFF'} ` +
`SU:${effects.shield ? 'DONE' : `TRY${S.shieldAttempts}`} ` +
`SΔ:${S.shieldTiming.lastClearToSendMs == null ? '-' : S.shieldTiming.lastClearToSendMs.toFixed(2)}ms ` +
`S↗:${S.shieldTiming.lastSendToOnMs == null ? '-' : S.shieldTiming.lastSendToOnMs.toFixed(1)}ms ` +
`Frozen:${effects.frozen ? 'YES' : 'NO'} ` +
`AF:${effects.antiFreeze ? 'ON' : 'OFF'} ` +
`AFΔ:${S.lastAntiFreezeReactionMs == null ? '-' : S.lastAntiFreezeReactionMs.toFixed(3)}ms ` +
`ZD:${C.statePollMs}/${C.viewScanMs}ms ` +
`UI:${C.uiUpdateMinMs}ms ` +
`ARV:${C.autoViewAntiRec ? 'MOUSE@460ms' : 'OFF'} ` +
`REC:${C.autoRecEnabled ? `ON@${C.autoRecIntervalMs}ms#${S.autoRecSends}` : 'OFF'} ` +
`IDs:${S.visibleEnemyCount}[${S.visibleEnemyOwnerIds.slice(0, 8).join(',')}${S.visibleEnemyCount > 8 ? ',…' : ''}] ` +
`ARdrop:${S.antiRecLoopLastSentOwners.length}[${S.antiRecLoopLastSentOwners.slice(0, 8).join(',')}] ` +
`FV:${C.autoViewFreeze ? 'MOUSE@2900ms/NO-AF' : 'OFF'} ` +
`SH:${hotkeyLabel(C.shieldHotkeyCode)} ` +
`FSH:${hotkeyLabel(C.fastShieldHotkeyCode)}${S.fastShieldRunning ? '(ON)' : ''}@${C.fastShieldIntervalMs}ms#${S.fastShieldSends} ` +
`P15:${hotkeyLabel(C.pushBurstHotkeyCode)}×${C.pushBurstCount}@${C.pushBurstIntervalMs}ms ` +
`G30+SH:${hotkeyLabel(C.growthHotkeyCode)}@NO-MOUSE ` +
`AP:${C.autoPelletEnabled ? 'ON' : 'OFF'}×${C.autoPelletAmount}/${C.autoPelletIntervalMs}ms TOTAL:${S.autoPelletTotal} ` +
`SMART:${hotkeyLabel(C.comboHotkeyCode)}${S.comboRunning ? '(RUN)' : ''} ` +
`Q:${S.comboQueue.length} ` +
`Mode:${S.lastComboSend ? (S.lastComboSend.shieldAtSend ? 'SHIELD=P+AR+F+V' : 'NO-SHIELD=AR+F+V') : '-'} ` +
`Sent:${S.lastComboSend ? S.lastComboSend.sentOrder.join('>') : '-'} ` +
`CMBerr:${S.lastComboBlock ? S.lastComboBlock.reason : '-'} ` +
`Mouse:${S.mouseWorld ? Math.round(now() - S.mouseWorld.t) + 'ms' : '—'} ` +
`drops:${S.lastDrop ? S.lastDrop.item : '-'}`;
}
function restartStatePoll() {
if (S.poll) {
clearInterval(S.poll);
S.poll = null;
}
const ms = Number(C.statePollMs);
if (Number.isFinite(ms) && ms > 0) {
S.poll = setInterval(
() => syncFromClient('poll'),
Math.max(1, Math.ceil(ms))
);
}
return {
enabled: !!S.poll,
pollMs: Number.isFinite(ms) ? ms : 0
};
}
function bootUi() {
if (document.body) createPanel();
else document.addEventListener('DOMContentLoaded', createPanel, { once: true });
}
installRenderTransformCapture();
installEarlySocketCapture();
loadNativeMaps();
bootUi();
// Capture phase handles key assignment and prevents matching game bindings.
PAGE.addEventListener('keydown', onGlobalHotkey, true);
restartStatePoll();
if (C.autoPelletEnabled) scheduleAutoPellet(500);
const API = {
version: '39.0.0-smart-nearest-cell-freeze',
state: getState,
shieldTiming: () => getState().shieldTiming,
shieldUntilEffectStatus: () => getState().shieldUntilEffectMode,
shieldOneDropStatus: () => getState().shieldUntilEffectMode,
resetShieldTiming: () => {
resetShieldTiming();
updatePanel();
return getState().shieldTiming;
},
setShieldUntilEffectTiming: (checkMs, minimumSendGapMs) => {
const check = Number(checkMs);
const gap = Number(minimumSendGapMs);
if (Number.isFinite(check)) {
C.shieldRetryCheckMs = Math.max(1, check);
}
if (Number.isFinite(gap)) {
C.shieldRetryMinSendGapMs = Math.max(20, gap);
}
if (C.autoShield && !flagsFromAnimations(liveAnimations() || 0).shield) {
scheduleShield('timing-updated');
}
updatePanel(true);
return getState().shieldUntilEffectMode;
},
setShieldFastMode: value => {
const enabled = !!value;
// Both modes remain packet/event-driven with no repeating poll.
C.statePollMs = 0;
if (enabled) {
C.shieldWatchdogGraceMs = 35;
C.shieldWatchdogRetryGapMs = 700;
} else {
C.shieldWatchdogGraceMs = 80;
C.shieldWatchdogRetryGapMs = 1200;
}
// Keep retrying until the server confirms the Shield effect.
C.shieldRetryScheduleMs = [];
C.shieldRetryMs = 20;
C.shieldRetryCheckMs = enabled ? 15 : 30;
C.shieldRetryMinSendGapMs = enabled ? 80 : 140;
C.shieldRetryMax = Infinity;
restartStatePoll();
updatePanel();
return {
enabled,
pollMs: C.statePollMs,
watchdogGraceMs: C.shieldWatchdogGraceMs,
retryScheduleMs: C.shieldRetryScheduleMs.slice()
};
},
setLowLagMode: value => {
const enabled = !!value;
if (enabled) {
C.statePollMs = 40;
C.wsSyncMinMs = 24;
C.wsNodeSyncDelayMs = 18;
C.viewScanMs = 45;
C.uiUpdateMinMs = 140;
C.shieldWatchdogRetryGapMs = 900;
} else {
// Zero-delay event-driven mode requested by the user.
C.statePollMs = 0;
C.wsSyncMinMs = 0;
C.wsNodeSyncDelayMs = 0;
C.viewScanMs = 0;
C.uiUpdateMinMs = 70;
C.shieldWatchdogRetryGapMs = 500;
}
restartStatePoll();
updatePanel(true);
return getState().performance;
},
setZeroDelayMode: value => {
const enabled = !!value;
if (enabled) {
C.statePollMs = 0;
C.wsSyncMinMs = 0;
C.wsNodeSyncDelayMs = 0;
C.viewScanMs = 0;
} else {
C.statePollMs = 40;
C.wsSyncMinMs = 24;
C.wsNodeSyncDelayMs = 18;
C.viewScanMs = 45;
}
restartStatePoll();
updatePanel(true);
return {
enabled,
pollMs: C.statePollMs,
wsSyncMinMs: C.wsSyncMinMs,
wsNodeSyncDelayMs: C.wsNodeSyncDelayMs,
viewScanMs: C.viewScanMs,
busyLoop: false
};
},
performance: () => getState().performance,
wsInfo: () => getState().ws,
maps: () => getState().nativeMaps,
selfCell: () => strictLiveOwnDropCell(),
recoverShield: () => {
S.shieldMissingSince = now() - C.shieldWatchdogGraceMs;
S.shieldLastWatchdogKickAt = 0;
return shieldWatchdog(
flagsFromAnimations(liveAnimations() || 0),
ownCells().length,
'manual-api'
);
},
resetDefense: () => {
resetDefenseState('manual-api-reset');
syncFromClient('manual-api-reset');
return getState();
},
rescan: forceRescan,
shield: () => sendShield('manual-api-shield'),
antiFreeze: () => sendAntiFreeze('manual-api-anti-freeze'),
antiFreezeTiming: () => ({
packetDirectEnabled: !!C.antiFreezePacketDirectEnabled,
reactionMs: S.lastAntiFreezeReactionMs,
reactionUs: S.lastAntiFreezeReactionUs,
frozenPacketAt: S.lastAntiFreezeFrozenPacketAt,
packetDirectSends: S.antiFreezePacketDirectSends,
packetDirectFallbacks: S.antiFreezePacketDirectFallbacks,
attempts: S.antiFreezeAttempts,
pendingRetry: !!S.antiFreezePending
}),
setAntiFreezePacketDirect: value => {
C.antiFreezePacketDirectEnabled = !!value;
updatePanel();
return C.antiFreezePacketDirectEnabled;
},
viewTick: () => viewDefenseTick('manual-api', true),
setViewSensitivity: level => {
const raw = Number(level);
const v = Math.max(-0.02, Math.min(0.95, Number.isFinite(raw) ? raw : 0));
C.viewAimDotMin = v;
C.viewEmergencyAimDot = v;
updatePanel();
return {
aimDotMin: C.viewAimDotMin,
emergencyAimDot: C.viewEmergencyAimDot
};
},
setAntiRecPerPlayerLock: ms => {
const raw = Number(ms);
C.viewAntiRecPerPlayerLockMs = Math.max(
1,
Number.isFinite(raw) ? raw : 460
);
// Reset only Anti-Rec owner locks so the new duration applies cleanly.
for (const key of Array.from(S.viewTargetLocks.keys())) {
if (String(key).startsWith('anti-rec-player:')) {
S.viewTargetLocks.delete(key);
}
}
if (C.autoViewAntiRec) {
scheduleAntiRecLoop(0, 'anti-rec-interval-changed');
}
updatePanel(true);
return {
antiRecPerPlayerLockMs: C.viewAntiRecPerPlayerLockMs,
effectiveMs: C.viewAntiRecPerPlayerLockMs,
mode: 'mouse-hover-global-and-player-lock'
};
},
setFreezeMouseLock: ms => {
const raw = Number(ms);
C.viewFreezePerPlayerLockMs = Math.max(
1,
Number.isFinite(raw) ? raw : 2900
);
for (const key of Array.from(S.viewTargetLocks.keys())) {
if (String(key).startsWith('freeze-player:')) {
S.viewTargetLocks.delete(key);
}
}
if (S.EntityTypes) {
S.viewGlobalLocks.delete(Number(S.EntityTypes.FREEZE));
}
updatePanel(true);
return {
freezePerPlayerLockMs: C.viewFreezePerPlayerLockMs,
mode: 'mouse-hover-global-and-player-lock'
};
},
visiblePlayers: () => ({
count: S.visibleEnemyCount,
ids: S.visibleEnemyOwnerIds.slice(),
updatedAt: S.visibleEnemyUpdatedAt
}),
antiRecLoopStatus: () => ({
enabled: !!C.autoViewAntiRec,
intervalMs: C.viewAntiRecPerPlayerLockMs,
timerActive: !!S.antiRecLoopTimer,
nextDueAt: S.antiRecLoopDueAt,
runs: S.antiRecLoopRuns,
totalDrops: S.antiRecLoopDrops,
lastRunAt: S.antiRecLoopLastRunAt,
lastSource: S.antiRecLoopLastSource,
visibleCount: S.antiRecLoopLastVisibleCount,
lastSentOwners: S.antiRecLoopLastSentOwners.slice()
}),
runAntiRecLoopNow: () => antiRecContinuousTick('manual-api'),
autoRecStatus: () => ({
enabled: !!C.autoRecEnabled,
intervalMs: C.autoRecIntervalMs,
timerActive: !!S.autoRecTimer,
nextDueAt: S.autoRecDueAt,
runs: S.autoRecRuns,
sends: S.autoRecSends,
lastSendAt: S.autoRecLastSendAt,
lastSource: S.autoRecLastSource
}),
setAutoRec,
toggleAutoRec: () => setAutoRec(!C.autoRecEnabled),
setAutoRecInterval: ms => {
const raw = Number(ms);
C.autoRecIntervalMs = Math.max(1, Number.isFinite(raw) ? raw : 1);
if (C.autoRecEnabled) scheduleAutoRecLoop(1, 'auto-rec-interval-changed');
updatePanel(true);
return C.autoRecIntervalMs;
},
runAutoRecNow: () => sendAutoRecPacket('manual-api'),
setViewForwardDrop: value => {
C.viewForwardDropEnabled = !!value;
updatePanel();
return {
configured: C.viewForwardDropEnabled,
effective: C.viewCenterDropOnly
? false
: C.viewForwardDropEnabled,
reason: C.viewCenterDropOnly
? 'mouse-hover-center-mode-forces-exact-center'
: ''
};
},
setViewForwardDistance: (base, max = C.viewForwardDropMax) => {
const nextBase = Number(base);
const nextMax = Number(max);
if (Number.isFinite(nextBase)) C.viewForwardDropBase = Math.max(0, nextBase);
if (Number.isFinite(nextMax)) C.viewForwardDropMax = Math.max(C.viewForwardDropMin, nextMax);
updatePanel();
return {
enabled: C.viewForwardDropEnabled,
base: C.viewForwardDropBase,
min: C.viewForwardDropMin,
max: C.viewForwardDropMax,
sizeFactor: C.viewForwardDropSizeFactor,
speedFactor: C.viewForwardDropSpeedFactor
};
},
setAutoViewAntiRec,
setAutoViewFreeze,
setViewCenterMode: value => {
C.viewCenterDropOnly = !!value;
C.viewTargetMode = C.viewCenterDropOnly
? 'mouse-hover-center'
: 'legacy-forward';
updatePanel(true);
return {
mode: C.viewTargetMode,
centerDropOnly: C.viewCenterDropOnly
};
},
shieldNow: () => sendShield('manual-api-shield'),
toggleAutoShield: () => {
const enabled = setAutoShield(!C.autoShield);
S.hotkeyMessage = `Auto Shield ${enabled ? 'ON' : 'OFF'}`;
updatePanel();
return enabled;
},
push15: () => sendPushBurst(15, 'manual-api-push15'),
pushBurst: count => sendPushBurst(count, 'manual-api-push-burst'),
beginShieldHotkeySet: () => beginHotkeyCapture('shield'),
beginPushHotkeySet: () => beginHotkeyCapture('push'),
beginGrowthHotkeySet: () => beginHotkeyCapture('growth'),
beginAutoPelletMouseHotkeySet: () => beginHotkeyCapture('pelletMouse'),
beginComboHotkeySet: () => beginHotkeyCapture('combo'),
growth: () => sendGrowth30Shield('manual-api-growth30-shield'),
autoPelletNow: () => sendAutoPellet30('manual-api-auto-pellet-now'),
setAutoPellet,
setAutoPelletWorld: (x, y) => setAutoPelletWorld(x, y, 'manual-api-world'),
captureAutoPelletWorld: () => captureAutoPelletWorldFromMouse('manual-api-set-at-mouse'),
getAutoPelletWorld: () => configuredAutoPelletWorld(),
toggleAutoPellet: () => setAutoPellet(!C.autoPelletEnabled),
resetAutoPelletCount: () => {
S.autoPelletPackets = 0;
S.autoPelletTotal = 0;
S.lastAutoPellet = null;
updatePanel(true);
return true;
},
combo: () => sendTargetedCombo('manual-api-smart-drop'),
comboStatus: () => getState().manualTargeting.combo,
comboInventory: () => comboInventoryStatus(),
portalEvidence: () => {
const baseline = new Set();
return {
inventory: portalInventoryCount(),
livePortalNodes: livePortalNodeIds().size,
latestSmartDrop: S.lastComboSend
};
},
cancelCombo: () => cancelTargetedCombo('manual-api-cancel'),
setShieldHotkey: code => {
const value = String(code || '');
if (!value) return clearHotkey('shield');
const ok = assignCapturedHotkey('shield', value);
return ok ? { code: C.shieldHotkeyCode, label: hotkeyLabel(C.shieldHotkeyCode) } : false;
},
setFastShieldHotkey: code => {
const value = String(code || '');
if (!value) return clearHotkey('fastShield');
const ok = assignCapturedHotkey('fastShield', value);
return ok ? { code: C.fastShieldHotkeyCode, label: hotkeyLabel(C.fastShieldHotkeyCode) } : false;
},
fastShieldOnce: () => fastShieldDropOnce('manual-api-fast-shield'),
toggleFastShield: () => toggleFastShieldDrop('manual-api-toggle'),
startFastShield: () => startFastShieldDrop('manual-api-start'),
stopFastShield: () => stopFastShieldDrop('manual-api-stop'),
setPushHotkey: code => {
const value = String(code || '');
if (!value) return clearHotkey('push');
const ok = assignCapturedHotkey('push', value);
return ok ? { code: C.pushBurstHotkeyCode, label: hotkeyLabel(C.pushBurstHotkeyCode) } : false;
},
setGrowthHotkey: code => {
const value = String(code || '');
if (!value) return clearHotkey('growth');
const ok = assignCapturedHotkey('growth', value);
return ok ? { code: C.growthHotkeyCode, label: hotkeyLabel(C.growthHotkeyCode) } : false;
},
setAutoPelletMouseHotkey: code => {
const value = String(code || '');
if (!value) return clearHotkey('pelletMouse');
const ok = assignCapturedHotkey('pelletMouse', value);
return ok ? {
code: C.autoPelletSetMouseHotkeyCode,
label: hotkeyLabel(C.autoPelletSetMouseHotkeyCode)
} : false;
},
setComboHotkey: code => {
const value = String(code || '');
if (!value) return clearHotkey('combo');
const ok = assignCapturedHotkey('combo', value);
return ok ? { code: C.comboHotkeyCode, label: hotkeyLabel(C.comboHotkeyCode) } : false;
},
cancelPushBurst: () => {
S.pushBurstToken++;
if (S.pushBurstTimer) clearTimeout(S.pushBurstTimer);
S.pushBurstTimer = null;
S.pushBurstRunning = false;
updatePanel();
return true;
},
setPushInterval: ms => {
C.pushBurstIntervalMs = Math.max(0, Number(ms) || 10.5);
updatePanel();
return C.pushBurstIntervalMs;
},
setPushEnabled: value => {
C.pushBurstEnabled = !!value;
updatePanel();
return C.pushBurstEnabled;
},
setShieldBreakEnabled,
setShieldBreakPlayerId,
setTargetAutoFreeze,
setTargetAutoAntiRec,
targetAutoFreezeNow: () => targetAutoFreezeTick('manual-api-target-freeze-now'),
targetAutoAntiRecNow: () => targetAutoAntiRecTick('manual-api-target-antirec-now'),
shieldBreakNow: () => {
if (!C.shieldBreakEnabled) return false;
return attemptShieldBreakPortal('manual-api-shield-break-now');
},
shieldBreakStatus: () => ({
enabled: !!C.shieldBreakEnabled,
playerId: normalizeShieldBreakPlayerId(C.shieldBreakPlayerId),
shieldOn: !!S.shieldBreakShieldOn,
portalSentThisShield: !!S.shieldBreakPortalSent,
attempts: S.shieldBreakAttempts,
sends: S.shieldBreakSends,
lastSend: S.shieldBreakLastSend,
lastBlock: S.shieldBreakLastBlock
}),
setAutoShield,
setAutoAntiFreeze,
config: C
};
PAGE[KEY] = API;
PAGE.__AAF = API;
try { window.__AAF = API; } catch (_) {}
if (C.autoViewAntiRec) {
scheduleAntiRecLoop(0, 'startup');
}
if (C.autoRecEnabled) {
scheduleAutoRecLoop(1, 'startup-auto-rec');
}
if (normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
queueMicrotask(() => {
const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
const player = configuredShieldBreakPlayer();
if (player && Number.isFinite(Number(player.animations))) {
if (C.shieldBreakEnabled) {
processShieldBreakAnimation(targetId, Number(player.animations), 'startup-shield-break-live-state');
}
if (C.targetAutoFreezeEnabled) {
processTargetIdAnimation(targetId, Number(player.animations), 'startup-target-freeze-live-state');
}
}
if (C.targetAutoAntiRecEnabled) {
scheduleTargetAntiRec(0, 'startup-target-antirec');
}
});
}
console.log('[AC Shield Until Effect v38.6.0] loaded');
})();