Skip to content

Instantly share code, notes, and snippets.

@19h
Created March 26, 2026 15:13
Show Gist options
  • Select an option

  • Save 19h/2b72c020b704665262f55934e1470390 to your computer and use it in GitHub Desktop.

Select an option

Save 19h/2b72c020b704665262f55934e1470390 to your computer and use it in GitHub Desktop.
cyberpunk-macos-arm64-2.3.1
/*
* Cyberpunk 2077 — Frida instrumentation script (current build)
*
* Subsystems:
* Weapon — zero ammo cost, configurable projectile fan, recoil nullification, CoF freeze
* Survival — player-specific health freeze at request-builder and executor levels
* Economy — money quantity multiplier on inventory-add path, XP replay on proficiency grants
* Teleport — physics-proxy scan, camera-relative movement, GPS target warp, sticky tracking
* Input — game input manager/backend key interception for hotkey-driven teleport
*
* Architecture:
* Config — runtime-tunable parameters
* Addresses — RVA table + lazy NativeFunction cache
* Memory helpers — scratch buffers, safe reads, quaternion/vector math
* PlayerTracker — entity ID + object pointer capture from ShootEvent and proxy ownership
* StateProxy — physics proxy lookup, state block read/write, position commit
* CameraReader — weak-handle traversal for world rotation quaternion
* GpsTracker — cached quest/player GPS path targets
* TeleportEngine — candidate scan, slot selection, directional/absolute warp, sticky loop
* HotkeyRouter — keycode dispatch from input hooks to teleport actions
* Hook installer — Interceptor.attach / .replace orchestration
* Public API — globalThis.tp + rpc.exports
*/
"use strict";
(() => {
if (globalThis.__cp2077ScriptLoaded) {
console.log("[cp2077] script already loaded; skipping duplicate initialization");
return;
}
globalThis.__cp2077ScriptLoaded = true;
// ─── Configuration ───────────────────────────────────────────────────────────
const Config = {
zeroAmmoCost: true,
projectileCount: 20,
moneyMultiplier: 1000,
experienceMultiplier: 1000,
disableRecoil: true,
disableConeOfFire: true,
disableFallDamage: true,
freezeHealth: true,
enforceHealthAtExecution: true,
teleportHotkeyEnabled: true,
logInputKeys: false,
logCameraQuatEveryMs: 0,
teleportHotkeyDistance: 5.0,
verbose: true,
hotkeys: {
forward: 104, // NumPad8
right: 102, // NumPad6
left: 100, // NumPad4
backward: 98, // NumPad2
target: 106, // NumStar
playerTarget: 111, // NumSlash
stickyTarget: [187, 108], // Equals | Separator
increase: 107, // NumPlus
decrease: 109, // NumMinus
},
};
// ─── RVA Table ───────────────────────────────────────────────────────────────
const RVA = Object.freeze({
onShoot: 0x0442420C,
consumeAmmo: 0x04423938,
getAmmoCost: 0x0440EFC4,
getProjectilesPerShot: 0x0440EFCC,
recoilKick: 0x04123944,
recoilReset: 0x04123E94,
accuracyUpdateJob: 0x042445BC,
buildSetStatPoolValue: 0x0416AE04,
buildChangeStatPoolValue:0x0416B02C,
executeSetStatPoolValue: 0x041767B4,
executeChangeStatPoolValue: 0x04176A58,
inventoryAddBackend: 0x03F53100,
funcGiveMoney: 0x03FA4498,
funcGiveItemByTdbid: 0x03FA5590,
funcGiveItems: 0x03FA57CC,
moneyItemIdHelper: 0x03FA3D84,
moneyItemIdLo: 0x089322F0,
moneyItemIdHi: 0x089322F8,
addProficiencyPoint: 0x046F144C,
isValidProxyId: 0x048D6C0C,
getProxyPointer: 0x048DBE1C,
getStateBlock: 0x048D79B4,
getStateValue: 0x048DC1E8,
buildWriteChain: 0x048DBEA0,
commitWriteChain: 0x048DBFBC,
writeStateValue: 0x049140D4,
inputCacheInputManager: 0x02C7336C,
inputCacheInputBackend: 0x02C751CC,
gpsGetCurrentPathTarget: 0x02C012C4,
});
const Offsets = Object.freeze({
shootEvent: { instigatorWeakHandle: 0x040, ammoCost: 0x0B0, projectilesPerShot: 0x180 },
gameObject: { entityId: 0x048 },
statRequest: { objectId: 0x020, statPoolType: 0x030, floatValue: 0x034 },
proxyLookup: { table: 0x09111CD0, generations: 1056784 },
playerObject: { cameraSystemWeakHandle: 1256, cameraComponentWeakHandle: 1192 },
cameraComponent: { worldPositionEncoded: 216, worldRotationQuat: 232 },
cameraSystem: { cachedRotationQuat: 1232 },
});
const STAT_POOL_HEALTH = 10;
const HEALTH_POOL_RUNTIME = 48;
const MONEY_CURRENCY_HASH = "0xF483CE936F7DF10F";
const ALL_SHAPES = uint64("0xFFFFFFFFFFFFFFFF");
// ─── Logging ─────────────────────────────────────────────────────────────────
const log = (msg) => console.log(`[cp2077] ${msg}`);
const tLog = (msg) => log(`tp: ${msg}`);
const once = (() => {
const seen = new Set();
return (msg) => { if (!seen.has(msg)) { seen.add(msg); log(msg); } };
})();
// ─── Address Resolution ──────────────────────────────────────────────────────
const base = Process.mainModule?.base ?? Process.enumerateModules()[0].base;
const addr = (rva) => base.add(rva);
/** Lazy NativeFunction cache keyed by RVA name. */
class NativeFunctions {
#cache = new Map();
get(name, retType, argTypes) {
if (!this.#cache.has(name)) {
this.#cache.set(name, new NativeFunction(addr(RVA[name]), retType, argTypes));
}
return this.#cache.get(name);
}
resetRecoil = (mgr, flag) => this.get("recoilReset", "pointer", ["pointer", "char"])(mgr, flag);
initMoneyItemId = (dst, hash) => this.get("moneyItemIdHelper", "uint64", ["pointer", "uint64"])(dst, hash);
addProficiency = (req, sys) => this.get("addProficiencyPoint", "pointer", ["pointer", "pointer"])(req, sys);
isValidProxyId = (tbl, id) => this.get("isValidProxyId", "bool", ["pointer", "uint"])(tbl, id);
getProxyPointer = (id) => this.get("getProxyPointer", "pointer", ["uint"])(id);
getStateBlock = (tbl, id) => this.get("getStateBlock", "pointer", ["pointer", "uint"])(tbl, id);
getStateValue(block, proxyId, stateId, outBuf, outLen) {
const fn = this.get("getStateValue", "int64", [
"pointer", "uint", "uint64", "uint64", "uint64", "pointer", "uint64",
]);
return Number(fn(block, proxyId, u64(0), u64(0), u64(stateId), outBuf, u64(outLen)));
}
buildWriteChain = (block, id) => this.get("buildWriteChain", "pointer", ["pointer", "int"])(block, id);
commitWriteChain = (id, delta) => this.get("commitWriteChain", "bool", ["uint", "uint64"])(id, delta);
writeStateValue(proxyId, block, delta, stateId, buf, bufLen) {
const fn = this.get("writeStateValue", "pointer", [
"uint", "pointer", "pointer", "uint64", "uint64", "uint64", "pointer", "uint64", "uchar",
]);
return fn(proxyId, block, delta, u64(0), ALL_SHAPES, u64(stateId), buf, u64(bufLen), 0);
}
executeSetStatPool(sys, objId, type, instigator, a, b, c, val) {
const fn = this.get("executeSetStatPoolValue", "bool", [
"pointer", "pointer", "int", "pointer", "bool", "bool", "bool", "float",
]);
return fn(sys, objId, type, instigator, a, b, c, val);
}
executeChangeStatPool(sys, objId, type, instigator, a, b, diff) {
const fn = this.get("executeChangeStatPoolValue", "bool", [
"pointer", "pointer", "int", "pointer", "bool", "bool", "float",
]);
return fn(sys, objId, type, instigator, a, b, diff);
}
}
const nf = new NativeFunctions();
// ─── Utility ─────────────────────────────────────────────────────────────────
const u64 = (v) => uint64(typeof v === "string" ? v : String(v));
const safeRead = (fn) => { try { return fn(); } catch { return null; } };
const safeReadPointer = (p) => safeRead(() => p.readPointer());
const isLiveObject = (p) => {
if (!p || p.isNull()) return false;
const vt = safeReadPointer(p);
return vt !== null && !vt.isNull();
};
// ─── Scratch Buffers ─────────────────────────────────────────────────────────
const Scratch = Object.freeze({
u8: Memory.alloc(1),
f32: Memory.alloc(4),
vec3: Memory.alloc(12),
quat: Memory.alloc(16),
});
// ─── Vector / Quaternion Math ────────────────────────────────────────────────
const Vec3 = {
read(ptr) { return { x: ptr.readFloat(), y: ptr.add(4).readFloat(), z: ptr.add(8).readFloat() }; },
write(ptr, v) { ptr.writeFloat(v.x); ptr.add(4).writeFloat(v.y); ptr.add(8).writeFloat(v.z); },
isFinite(v) { return Number.isFinite(v.x) && Number.isFinite(v.y) && Number.isFinite(v.z); },
zero() { return { x: 0, y: 0, z: 0 }; },
add(a, b) { return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; },
scale(v, s) { return { x: v.x * s, y: v.y * s, z: v.z * s }; },
norm2d(v) {
const len = Math.hypot(v.x, v.y);
return len < 1e-6 ? { x: 0, y: 1, z: 0 } : { x: v.x / len, y: v.y / len, z: 0 };
},
format(v, prec = 2) { return `(${v.x.toFixed(prec)}, ${v.y.toFixed(prec)}, ${v.z.toFixed(prec)})`; },
};
const Quat = {
read(ptr) {
return {
x: ptr.readFloat(), y: ptr.add(4).readFloat(),
z: ptr.add(8).readFloat(), w: ptr.add(12).readFloat(),
};
},
isNormalized(q) {
if (!Number.isFinite(q.x) || !Number.isFinite(q.y) || !Number.isFinite(q.z) || !Number.isFinite(q.w))
return false;
const len = Math.hypot(q.x, q.y, q.z, q.w);
return len > 0.7 && len < 1.3;
},
rotate(q, v) {
const u = { x: q.x, y: q.y, z: q.z };
const s = q.w;
const dot = u.x * v.x + u.y * v.y + u.z * v.z;
const uu = u.x * u.x + u.y * u.y + u.z * u.z;
const cx = { x: u.y * v.z - u.z * v.y, y: u.z * v.x - u.x * v.z, z: u.x * v.y - u.y * v.x };
const k = s * s - uu;
return {
x: 2 * dot * u.x + k * v.x + 2 * s * cx.x,
y: 2 * dot * u.y + k * v.y + 2 * s * cx.y,
z: 2 * dot * u.z + k * v.z + 2 * s * cx.z,
};
},
format(q, prec = 6) {
return `x=${q.x.toFixed(prec)} y=${q.y.toFixed(prec)} z=${q.z.toFixed(prec)} w=${q.w.toFixed(prec)}`;
},
};
// ─── Weak Handle Traversal ───────────────────────────────────────────────────
function readWeakHandlePointee(basePtr, offset) {
if (!isLiveObject(basePtr)) return null;
const handle = basePtr.add(offset);
const pointee = safeReadPointer(handle);
const refCount = safeReadPointer(handle.add(Process.pointerSize));
if (!pointee || pointee.isNull() || !refCount || refCount.isNull()) return null;
return isLiveObject(pointee) ? pointee : null;
}
// ─── Player Tracker ──────────────────────────────────────────────────────────
class PlayerTracker {
entityId = "1";
objectPtr = null;
get hasObject() { return isLiveObject(this.objectPtr); }
captureFromShootEvent(evtPtr) {
try {
const obj = evtPtr.add(Offsets.shootEvent.instigatorWeakHandle).readPointer();
if (obj.isNull()) return;
this.#adoptObject(obj);
} catch (e) { log(`Failed to capture player from ShootEvent: ${e}`); }
}
captureFromProxyOwner(proxyId) {
const owner = this.#proxyOwner(proxyId);
if (!owner) return false;
this.#adoptObject(owner);
return true;
}
matchesEntityId(entityIdStr) { return this.entityId === entityIdStr; }
matchesStatRequest(reqPtr, statPoolType) {
if (statPoolType !== STAT_POOL_HEALTH) return false;
return safeRead(() => reqPtr.add(Offsets.statRequest.objectId).readU64().toString()) === this.entityId;
}
matchesStatsObjectId(idPtr) {
if (!idPtr || idPtr.isNull()) return false;
return safeRead(() => idPtr.readU64().toString()) === this.entityId;
}
matchesObjectHandle(handlePtr) {
if (!this.entityId) return true;
const obj = safeRead(() => handlePtr.add(72).readPointer());
if (!obj || obj.isNull()) return false;
return safeRead(() => obj.add(Offsets.gameObject.entityId).readU64().toString()) === this.entityId;
}
#adoptObject(obj) {
if (!this.objectPtr || this.objectPtr.isNull() || !obj.equals(this.objectPtr)) {
this.objectPtr = obj;
}
const eid = safeRead(() => obj.add(Offsets.gameObject.entityId).readU64().toString());
if (eid && eid !== this.entityId) {
this.entityId = eid;
log(`Tracked player EntityID=${eid}`);
}
}
#proxyOwner(proxyId) {
const proxy = safeRead(() => nf.getProxyPointer(proxyId));
if (!isLiveObject(proxy)) return null;
const owner = safeRead(() => proxy.add(8).readPointer());
return isLiveObject(owner) ? owner : null;
}
}
const player = new PlayerTracker();
// ─── Money Item Cache ────────────────────────────────────────────────────────
class MoneyCache {
#lo = null;
#hi = null;
init() {
try {
nf.initMoneyItemId(ptr(0), uint64(MONEY_CURRENCY_HASH));
this.#lo = addr(RVA.moneyItemIdLo).readU64().toString();
this.#hi = addr(RVA.moneyItemIdHi).readU64().toString();
log(`Tracked money ItemID=${this.#lo}:${this.#hi}`);
} catch (e) { log(`Failed to init money ItemID cache: ${e}`); }
}
matches(itemModParamsPtr) {
if (!this.#lo || !this.#hi) return false;
return safeRead(() => {
const lo = itemModParamsPtr.add(8).readU64().toString();
const hi = itemModParamsPtr.add(16).readU64().toString();
return lo === this.#lo && hi === this.#hi;
}) ?? false;
}
tryMultiply(itemModParamsPtr) {
if (!this.matches(itemModParamsPtr)) return false;
const qtyPtr = itemModParamsPtr.add(24);
const old = qtyPtr.readS32();
if (old <= 0) return false;
const next = Math.min(old * Config.moneyMultiplier, 0x7FFFFFFF);
if (next === old) return false;
qtyPtr.writeS32(next);
if (Config.verbose) log(`Money quantity ${old}${next}`);
return true;
}
}
const money = new MoneyCache();
// ─── State Proxy System ──────────────────────────────────────────────────────
class StateProxy {
get lookupTable() { return base.add(Offsets.proxyLookup.table).readPointer(); }
readBlock(table, proxyId) {
const block = nf.getStateBlock(table, proxyId);
return block.isNull() ? null : block;
}
readU8(block, proxyId, stateId) {
return nf.getStateValue(block, proxyId, stateId, Scratch.u8, 1) ? Scratch.u8.readU8() : null;
}
readF32(block, proxyId, stateId) {
return nf.getStateValue(block, proxyId, stateId, Scratch.f32, 4) ? Scratch.f32.readFloat() : null;
}
readVec3(block, proxyId, stateId) {
if (!nf.getStateValue(block, proxyId, stateId, Scratch.vec3, 12)) return null;
return Vec3.read(Scratch.vec3);
}
readQuat(block, proxyId, stateId) {
if (!nf.getStateValue(block, proxyId, stateId, Scratch.quat, 16)) return null;
return Quat.read(Scratch.quat);
}
writePositionAndZeroVelocity(proxyId, block, position) {
const delta = nf.buildWriteChain(block, proxyId);
if (delta.isNull()) throw new Error("buildWriteChain failed");
Vec3.write(Scratch.vec3, position);
nf.writeStateValue(proxyId, block, delta, 1, Scratch.vec3, 12);
Vec3.write(Scratch.vec3, Vec3.zero());
nf.writeStateValue(proxyId, block, delta, 4, Scratch.vec3, 12);
nf.commitWriteChain(proxyId, u64(delta));
}
}
const stateProxy = new StateProxy();
// ─── Camera Reader ───────────────────────────────────────────────────────────
class CameraReader {
#timer = null;
readWorldQuat() {
// Prefer camera system path
const sysPtr = readWeakHandlePointee(player.objectPtr, Offsets.playerObject.cameraSystemWeakHandle);
if (sysPtr) {
const q = safeRead(() => Quat.read(sysPtr.add(Offsets.cameraSystem.cachedRotationQuat)));
if (q && Quat.isNormalized(q)) return q;
}
// Fall back to camera component
const compPtr = readWeakHandlePointee(player.objectPtr, Offsets.playerObject.cameraComponentWeakHandle);
if (!compPtr) return null;
const q = safeRead(() => Quat.read(compPtr.add(Offsets.cameraComponent.worldRotationQuat)));
return (q && Quat.isNormalized(q)) ? q : null;
}
get forward() {
const q = this.readWorldQuat();
return q ? Quat.rotate(q, { x: 0, y: 1, z: 0 }) : null;
}
get right() {
const q = this.readWorldQuat();
return q ? Quat.rotate(q, { x: 1, y: 0, z: 0 }) : null;
}
startLogging(ms) {
this.stopLogging();
if (!Number.isFinite(ms) || ms <= 0) return false;
Config.logCameraQuatEveryMs = ms;
this.#timer = setInterval(() => {
const q = this.readWorldQuat();
tLog(q ? `camera quat: ${Quat.format(q)}` : "camera quat: null");
}, ms);
tLog(`camera quat logging every ${ms}ms`);
return true;
}
stopLogging() {
if (this.#timer !== null) { clearInterval(this.#timer); this.#timer = null; }
Config.logCameraQuatEveryMs = 0;
return true;
}
getDebugInfo() {
const out = { playerObjectPtr: player.objectPtr, playerObjectValid: player.hasObject };
if (!out.playerObjectValid) return out;
for (const [label, offset, quatOffset] of [
["cameraSystem", Offsets.playerObject.cameraSystemWeakHandle, Offsets.cameraSystem.cachedRotationQuat],
["cameraComponent", Offsets.playerObject.cameraComponentWeakHandle, Offsets.cameraComponent.worldRotationQuat],
]) {
const ptr = readWeakHandlePointee(player.objectPtr, offset);
out[`${label}Ptr`] = ptr;
out[`${label}Valid`] = isLiveObject(ptr);
if (ptr) {
const q = safeRead(() => Quat.read(ptr.add(quatOffset)));
out[`${label}Quat`] = q;
out[`${label}QuatNormalized`] = q ? Quat.isNormalized(q) : false;
}
}
return out;
}
}
const camera = new CameraReader();
// ─── GPS Tracker ─────────────────────────────────────────────────────────────
class GpsTracker {
#targets = { 0: null, 1: null };
get quest() { return this.#targets[0]; }
get tracked() { return this.#targets[1]; }
get(type) { return this.#targets[type] ?? null; }
getAll() { return { ...this.#targets }; }
get preferred() {
if (this.#targets[0]) return { type: 0, position: this.#targets[0], label: "quest" };
if (this.#targets[1]) return { type: 1, position: this.#targets[1], label: "tracked" };
return null;
}
update(targetType, ptr) {
try {
const pos = Vec3.read(ptr);
if (!Vec3.isFinite(pos)) return;
const prev = this.#targets[targetType];
const changed = !prev ||
Math.abs(prev.x - pos.x) > 0.01 ||
Math.abs(prev.y - pos.y) > 0.01 ||
Math.abs(prev.z - pos.z) > 0.01;
this.#targets[targetType] = pos;
if (changed) tLog(`gps target[${targetType}] → ${Vec3.format(pos)}`);
} catch { /* ignore */ }
}
}
const gps = new GpsTracker();
// ─── Teleport Engine ─────────────────────────────────────────────────────────
class TeleportEngine {
#candidates = [];
#activeSlot = -1;
#saved = new Map();
#stickyTimer = null;
get candidates() { return this.#candidates; }
get isStickyActive() { return this.#stickyTimer !== null; }
scan(limit = 0) {
const table = stateProxy.lookupTable;
if (table.isNull()) throw new Error("proxy lookup is null");
const out = [];
for (let low = 0; low <= 0xFFFF; low++) {
const gen = table.add(Offsets.proxyLookup.generations + low * 2).readU16();
const proxyId = ((gen << 16) | low) >>> 0;
if (!nf.isValidProxyId(table, proxyId)) continue;
const block = stateProxy.readBlock(table, proxyId);
if (!block) continue;
// Filter: movable humanoid proxies only
if (stateProxy.readU8(block, proxyId, 26) !== 1) continue;
const height = stateProxy.readF32(block, proxyId, 29);
const radius = stateProxy.readF32(block, proxyId, 30);
const pos = stateProxy.readVec3(block, proxyId, 1);
const rot = stateProxy.readQuat(block, proxyId, 2);
if (!height || !radius || !pos || !rot) continue;
if (height < 1.0 || height > 2.4 || radius < 0.2 || radius > 0.7) continue;
out.push({ proxyId, stateBlock: block, pos, rot, height, radius });
// Opportunistic player-object adoption
if (!player.hasObject) {
player.captureFromProxyOwner(proxyId);
}
if (limit && out.length >= limit) break;
}
this.#candidates = out;
return out;
}
#ensureCandidates() {
if (this.#candidates.length === 0) this.scan();
return this.#candidates;
}
#refresh(slot) {
const c = this.#ensureCandidates()[slot];
if (!c) throw new Error(`invalid slot ${slot}`);
const table = stateProxy.lookupTable;
if (table.isNull() || !nf.isValidProxyId(table, c.proxyId))
throw new Error("proxy no longer valid");
c.stateBlock = stateProxy.readBlock(table, c.proxyId);
c.pos = stateProxy.readVec3(c.stateBlock, c.proxyId, 1);
c.rot = stateProxy.readQuat(c.stateBlock, c.proxyId, 2);
if (!c.stateBlock || !c.pos || !c.rot) throw new Error("candidate state unavailable");
return c;
}
#requireActive() {
if (this.#activeSlot < 0) {
const cands = this.#ensureCandidates();
if (cands.length === 0) throw new Error("no teleport candidates found");
tLog("auto-selecting slot #0");
this.select(0);
}
return this.#refresh(this.#activeSlot);
}
select(slot) {
const c = this.#refresh(slot);
player.captureFromProxyOwner(c.proxyId);
this.#activeSlot = slot;
tLog(`selected ${this.#formatCandidate(slot, c)}${player.hasObject ? " [linked]" : " [no link]"}`);
return true;
}
probe(slot, dz = 1.5) {
const c = this.#refresh(slot);
this.#saved.set(c.proxyId, { ...c.pos });
stateProxy.writePositionAndZeroVelocity(c.proxyId, c.stateBlock, Vec3.add(c.pos, { x: 0, y: 0, z: dz }));
tLog(`probed slot #${slot} dz=${dz}`);
return true;
}
restore(slot) {
const c = this.#refresh(slot);
const saved = this.#saved.get(c.proxyId);
if (!saved) throw new Error(`no saved position for slot ${slot}`);
stateProxy.writePositionAndZeroVelocity(c.proxyId, c.stateBlock, saved);
tLog(`restored slot #${slot}`);
return true;
}
forward(distance, flat = true) {
const c = this.#requireActive();
let fwd = camera.forward ?? Quat.rotate(c.rot, { x: 0, y: 1, z: 0 });
if (flat) fwd = Vec3.norm2d(fwd);
const dst = Vec3.add(c.pos, Vec3.scale(fwd, distance));
stateProxy.writePositionAndZeroVelocity(c.proxyId, c.stateBlock, dst);
return dst;
}
local(lx, ly, lz) {
const c = this.#requireActive();
const dst = Vec3.add(c.pos, Quat.rotate(c.rot, { x: lx, y: ly, z: lz }));
stateProxy.writePositionAndZeroVelocity(c.proxyId, c.stateBlock, dst);
return dst;
}
delta(dx, dy, dz) {
const c = this.#requireActive();
const dst = Vec3.add(c.pos, { x: dx, y: dy, z: dz });
stateProxy.writePositionAndZeroVelocity(c.proxyId, c.stateBlock, dst);
return dst;
}
abs(x, y, z) {
const c = this.#requireActive();
const dst = { x, y, z };
stateProxy.writePositionAndZeroVelocity(c.proxyId, c.stateBlock, dst);
return dst;
}
toTarget(type = 0, zOffset = 1.0) {
const pos = gps.get(type);
if (!pos) throw new Error(`no cached GPS target for type ${type}`);
return this.abs(pos.x, pos.y, pos.z + zOffset);
}
toggleSticky() { return this.#stickyTimer !== null ? this.stopSticky() : this.startSticky(); }
startSticky() {
if (this.#stickyTimer !== null) return true;
this.#stickyTimer = setInterval(() => {
const t = gps.preferred;
if (!t) return;
try { this.toTarget(t.type); } catch (e) { tLog(`sticky failed: ${e}`); this.stopSticky(); }
}, 33);
tLog("sticky target enabled (30 FPS)");
return true;
}
stopSticky() {
if (this.#stickyTimer !== null) { clearInterval(this.#stickyTimer); this.#stickyTimer = null; }
tLog("sticky target disabled");
return true;
}
getActive() {
const c = this.#requireActive();
const line = this.#formatCandidate(this.#activeSlot, c);
console.log(line);
return line;
}
list(limit = 32) {
return this.scan(limit).map((c, i) => this.#formatCandidate(i, c));
}
#formatCandidate(slot, c) {
return `#${slot} id=0x${(c.proxyId >>> 0).toString(16).padStart(8, "0")} ` +
`pos=${Vec3.format(c.pos)} h=${c.height.toFixed(2)} r=${c.radius.toFixed(2)}`;
}
}
const tp = new TeleportEngine();
// ─── Hotkey Router ───────────────────────────────────────────────────────────
const INPUT_KEY_NAMES = {
98: "NumPad2", 100: "NumPad4", 102: "NumPad6", 104: "NumPad8",
106: "NumStar", 107: "NumPlus", 108: "Separator", 109: "NumMinus",
111: "NumSlash", 187: "Equals",
};
class HotkeyRouter {
#state = Object.create(null);
#mgrSeen = false;
#backendSeen = false;
get snapshot() {
return Object.fromEntries(
Object.entries(Config.hotkeys).map(([name, code]) => [name, { keyCode: code, down: !!this.#state[name] }])
);
}
onInput(source, key, action) {
if (source === "mgr" && !this.#mgrSeen) { this.#mgrSeen = true; tLog("manager input hook alive"); }
if (source === "backend" && !this.#backendSeen) { this.#backendSeen = true; tLog("backend input hook alive"); }
if (Config.logInputKeys && (action === 1 || action === 2)) {
const act = action === 1 ? "press" : "release";
tLog(`${source} ${act} ${INPUT_KEY_NAMES[key] ?? `Key${key}`} (${key})`);
}
if (!Config.teleportHotkeyEnabled) return;
for (const [name, code] of Object.entries(Config.hotkeys)) {
if (!(Array.isArray(code) ? code.includes(key) : code === key)) continue;
if (action === 1 && !this.#state[name]) { this.#state[name] = true; this.#dispatch(name); }
else if (action === 2) { this.#state[name] = false; }
break;
}
}
#dispatch(name) {
const d = Config.teleportHotkeyDistance;
const handlers = {
increase: () => { Config.teleportHotkeyDistance += 1; tLog(`distance → ${Config.teleportHotkeyDistance.toFixed(1)}`); },
decrease: () => { Config.teleportHotkeyDistance = Math.max(1, d - 1); tLog(`distance → ${Config.teleportHotkeyDistance.toFixed(1)}`); },
forward: () => { const p = tp.forward(d, true); tLog(`forward → ${Vec3.format(p)}`); },
backward: () => { const p = tp.forward(-d, true); tLog(`backward → ${Vec3.format(p)}`); },
right: () => { const r = Vec3.norm2d(camera.right ?? { x: 1, y: 0, z: 0 }); const p = tp.delta(r.x * d, r.y * d, 0); tLog(`right → ${Vec3.format(p)}`); },
left: () => { const r = Vec3.norm2d(camera.right ?? { x: 1, y: 0, z: 0 }); const p = tp.delta(-r.x * d, -r.y * d, 0); tLog(`left → ${Vec3.format(p)}`); },
target: () => { const pos = gps.quest; if (!pos) { tLog("no quest target"); return; } const p = tp.abs(pos.x, pos.y, pos.z + 1); tLog(`target → ${Vec3.format(p)}`); },
playerTarget: () => { const pos = gps.tracked; if (!pos) { tLog("no tracked target"); return; } const p = tp.abs(pos.x, pos.y, pos.z + 1); tLog(`player target → ${Vec3.format(p)}`); },
stickyTarget: () => tp.toggleSticky(),
};
try {
handlers[name]?.();
} catch (e) {
tLog(`${name} failed: ${e}`);
}
}
}
const hotkeys = new HotkeyRouter();
// ─── Hook Installation ───────────────────────────────────────────────────────
function installHooks() {
const at = (rva) => addr(RVA[rva]);
// ── Weapon: OnShoot event patching ──
Interceptor.attach(at("onShoot"), {
onEnter(args) {
const evt = args[1];
if (evt.isNull()) return;
player.captureFromShootEvent(evt);
try {
if (Config.zeroAmmoCost) {
const p = evt.add(Offsets.shootEvent.ammoCost);
const old = p.readU16();
if (old !== 0) { p.writeU16(0); if (Config.verbose) log(`OnShoot: ammo ${old} → 0`); }
}
if (Config.projectileCount > 0) {
const p = evt.add(Offsets.shootEvent.projectilesPerShot);
const n = Config.projectileCount & 0xFF;
const old = p.readU8();
if (old !== n) { p.writeU8(n); if (Config.verbose) log(`OnShoot: proj ${old}${n}`); }
}
} catch (e) { log(`OnShoot patch failed: ${e}`); }
},
});
// ── Weapon: ammo consumption ──
if (Config.zeroAmmoCost) {
Interceptor.attach(at("consumeAmmo"), { onEnter() { this.context.x1 = 0; } });
once("Ammo helper hooked: forced arg to 0");
Interceptor.attach(at("getAmmoCost"), { onLeave(r) { r.replace(0); } });
once("GetAmmoCost → 0");
}
// ── Weapon: projectile fan ──
if (Config.projectileCount > 0) {
Interceptor.attach(at("getProjectilesPerShot"), { onLeave(r) { r.replace(Config.projectileCount & 0xFF); } });
once(`GetProjectilesPerShot → ${Config.projectileCount & 0xFF}`);
}
// ── Weapon: recoil nullification ──
if (Config.disableRecoil) {
Interceptor.attach(at("recoilKick"), {
onEnter(args) { this.mgr = args[0]; },
onLeave(r) { try { nf.resetRecoil(this.mgr, 0); r.replace(0); } catch (e) { log(`Recoil reset failed: ${e}`); } },
});
once("Recoil kick → immediate reset");
}
// ── Weapon: cone-of-fire freeze ──
if (Config.disableConeOfFire) {
Interceptor.replace(at("accuracyUpdateJob"),
new NativeCallback(() => {}, "void", ["pointer", "pointer", "uint", "uint", "pointer"]));
once("Accuracy update job replaced: CoF frozen");
}
// ── Survival: stat pool request builders ──
if (Config.freezeHealth) {
Interceptor.attach(at("buildSetStatPoolValue"), {
onEnter(args) { this.req = args[0]; this.type = args[2].toInt32(); },
onLeave() { if (player.matchesStatRequest(this.req, this.type)) safeRead(() => this.req.add(Offsets.statRequest.floatValue).writeFloat(1.0)); },
});
once("SetSPValue builder: player health → 100%");
Interceptor.attach(at("buildChangeStatPoolValue"), {
onEnter(args) { this.req = args[0]; this.type = args[2].toInt32(); },
onLeave() {
if (!player.matchesStatRequest(this.req, this.type)) return;
const diff = safeRead(() => this.req.add(Offsets.statRequest.floatValue).readFloat());
if (diff !== null && diff < 0) this.req.add(Offsets.statRequest.floatValue).writeFloat(0);
},
});
once("ChangeSPValue builder: negative player health deltas → 0");
}
// ── Survival: execution-level health enforcement ──
if (Config.enforceHealthAtExecution && (Config.freezeHealth || Config.disableFallDamage)) {
Interceptor.replace(at("executeSetStatPoolValue"),
new NativeCallback((sys, objId, type, ins, a, b, c, val) => {
if (type === HEALTH_POOL_RUNTIME && player.matchesStatsObjectId(objId)) {
if (Config.verbose) log("Blocked direct health set");
return true;
}
return nf.executeSetStatPool(sys, objId, type, ins, a, b, c, val);
}, "bool", ["pointer", "pointer", "int", "pointer", "bool", "bool", "bool", "float"]));
Interceptor.replace(at("executeChangeStatPoolValue"),
new NativeCallback((sys, objId, type, ins, a, b, diff) => {
if (type === HEALTH_POOL_RUNTIME && player.matchesStatsObjectId(objId) && diff < 0) {
if (Config.verbose) log(`Blocked negative health Δ: ${diff}`);
return true;
}
return nf.executeChangeStatPool(sys, objId, type, ins, a, b, diff);
}, "bool", ["pointer", "pointer", "int", "pointer", "bool", "bool", "float"]));
once("Execution-level health enforcement active");
}
// ── Economy: money multiplier ──
if (Config.moneyMultiplier > 1) {
Interceptor.attach(at("inventoryAddBackend"), {
onEnter(args) { if (!args[1].isNull()) try { money.tryMultiply(args[1]); } catch (e) { log(`Money mul failed: ${e}`); } },
});
once("Money multiplier on inventory add backend");
}
// ── Economy: XP replay ──
let xpDepth = 0;
if (Config.experienceMultiplier > 1) {
Interceptor.attach(at("addProficiencyPoint"), {
onEnter(args) { this.req = args[0]; this.sys = args[1]; this.replayed = xpDepth !== 0; },
onLeave() {
if (this.replayed) return;
xpDepth++;
try { for (let i = 1; i < Config.experienceMultiplier; i++) nf.addProficiency(this.req, this.sys); }
catch (e) { log(`XP mul failed: ${e}`); }
finally { xpDepth--; }
},
});
once("XP multiplier on AddProficiencyPoint");
}
// ── Input: hotkey hooks ──
if (Config.teleportHotkeyEnabled) {
const argU32 = (v) => (v?.toUInt32 ? v.toUInt32() : Number(v)) >>> 0;
for (const rva of ["inputCacheInputManager", "inputCacheInputBackend"]) {
const source = rva.includes("Manager") ? "mgr" : "backend";
Interceptor.attach(at(rva), {
onEnter(args) { hotkeys.onInput(source, argU32(args[1]), argU32(args[2])); },
});
}
once("Teleport hotkeys via game input manager + backend");
}
// ── GPS: target cache ──
Interceptor.attach(at("gpsGetCurrentPathTarget"), {
onEnter(args) { this.type = (args[1]?.toUInt32 ? args[1].toUInt32() : Number(args[1])) >>> 0; this.out = args[2]; },
onLeave(r) { if (r.toInt32?.() !== 0) gps.update(this.type, this.out); },
});
once("GPS target hook active");
}
// ─── Public API ──────────────────────────────────────────────────────────────
function exposeApi() {
globalThis.tp = {
scan: (limit) => { const c = tp.scan(limit || 0); tLog(`found ${c.length} candidates`); return tp.list(c.length); },
list: (limit) => { const l = tp.list(limit || 32); l.forEach((s) => console.log(s)); return l; },
select: (slot) => tp.select(Number(slot)),
probe: (slot, dz) => tp.probe(Number(slot), dz ?? 1.5),
restore: (slot) => tp.restore(Number(slot)),
active: () => tp.getActive(),
forward: (d, flat) => tp.forward(Number(d), flat !== false),
local: (x, y, z) => tp.local(Number(x), Number(y), Number(z)),
delta: (dx, dy, dz) => tp.delta(Number(dx), Number(dy), Number(dz)),
abs: (x, y, z) => tp.abs(Number(x), Number(y), Number(z)),
look: () => { const f = camera.forward; console.log(f); return f; },
player: () => { const o = { entityId: player.entityId, object: player.objectPtr }; console.log(o); return o; },
camera: () => { const o = camera.getDebugInfo(); console.log(o); return o; },
targets: () => { const t = gps.getAll(); console.log(t); return t; },
target: (type) => { const t = gps.get(type ?? 0); console.log(t); return t; },
toTarget: (type, zOff) => tp.toTarget(type ?? 0, zOff ?? 1.0),
stickyTarget:(enable) => enable === undefined ? tp.toggleSticky() : (enable ? tp.startSticky() : tp.stopSticky()),
watchCamera: (ms) => camera.startLogging(ms ?? 1000),
stopCamera: () => camera.stopLogging(),
keys: () => { const s = hotkeys.snapshot; console.log(s); return s; },
};
rpc.exports = {
tpscan: (limit) => tp.scan(limit || 0).map((c, i) => ({ slot: i, proxyId: c.proxyId >>> 0, position: c.pos, height: c.height, radius: c.radius })),
tpselect: (slot) => (tp.select(Number(slot)), true),
tpprobe: (slot, dz) => (tp.probe(Number(slot), dz ?? 1.5), true),
tprestore: (slot) => (tp.restore(Number(slot)), true),
tpforward: (d) => tp.forward(Number(d), true),
tplocal: (x, y, z) => tp.local(Number(x), Number(y), Number(z)),
tpdelta: (dx, dy, dz) => tp.delta(Number(dx), Number(dy), Number(dz)),
tpabs: (x, y, z) => tp.abs(Number(x), Number(y), Number(z)),
tptarget: (type) => gps.get(type ?? 0),
tptotarget: (type, zOff) => tp.toTarget(type ?? 0, zOff ?? 1.0),
tpstickytarget: (enable) => enable === undefined ? tp.toggleSticky() : (enable ? tp.startSticky() : tp.stopSticky()),
tpwatchcamera: (ms) => camera.startLogging(ms ?? 1000),
tpstopcamera: () => camera.stopLogging(),
};
}
// ─── Bootstrap ───────────────────────────────────────────────────────────────
log(`base=${base}`);
log(`OnShoot=${addr(RVA.onShoot)} ConsumeAmmo=${addr(RVA.consumeAmmo)}`);
log(`AmmoCost=${addr(RVA.getAmmoCost)} ProjPerShot=${addr(RVA.getProjectilesPerShot)}`);
log(`RecoilKick=${addr(RVA.recoilKick)} AccuracyJob=${addr(RVA.accuracyUpdateJob)}`);
log(`SetSP=${addr(RVA.buildSetStatPoolValue)} ChangeSP=${addr(RVA.buildChangeStatPoolValue)}`);
log(`InvAdd=${addr(RVA.inventoryAddBackend)} Money=${addr(RVA.funcGiveMoney)} XP=${addr(RVA.addProficiencyPoint)}`);
log(`Player EntityID=${player.entityId} (assumed until observed)`);
log(`Fall damage suppression=${Config.freezeHealth || Config.disableFallDamage ? "on" : "off"}`);
if (Config.teleportHotkeyEnabled) {
log(`Teleport hotkeys: 8/6/4/2=move *=quest /=tracked ==sticky +/-=distance (${Config.teleportHotkeyDistance.toFixed(1)})`);
}
money.init();
installHooks();
exposeApi();
if (Config.logCameraQuatEveryMs > 0) camera.startLogging(Config.logCameraQuatEveryMs);
log("All hooks active");
tLog("tp.list() tp.select(n) tp.forward(d) tp.local(x,y,z) tp.abs(x,y,z) tp.look() tp.camera() tp.target() tp.toTarget() tp.stickyTarget() tp.watchCamera(ms)");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment