hangmu-docker / patch_control.mjs
bep40's picture
Upload patch_control.mjs
3d821c0 verified
Raw History Blame Contribute Delete
10.9 kB
/* patch_control.mjs — v17: joystick theo hướng camera + nhân vật kiểu cầu thủ MU
FIX A (joystick lệch hướng):
Trước đây input joystick map thẳng {x:dx, z:-dy} vào tọa độ THẾ GIỚI →
atan2(mx,mz) → kéo "lên màn hình" (dy<0) = +z thế giới = đi RA SÂN, trong khi
camera nhìn về -z → ngược với cảm nhận màn hình.
FIX: xoay hệ quy chiếu theo yaw camera (me.rot): kéo lên = đi xa camera
(forward), kéo phải = đi bên phải màn hình (right = forward × up).
FIX B (nhân vật chưa giống hangmu.online):
Model cũ chỉ là capsule trơn. Nâng cấp thành cầu thủ MU: tóc đen, áo đỏ
bo tròn + cổ trắng, tay áo đỏ (cẳng tay màu da), quần đùi đen, tất đỏ,
giày trắng — giữ nguyên yOff/gy/held ở render scope (tránh crash v15).
Nếu pattern lệch -> exit(1) để build fail. */
import fs from 'node:fs'
function patchFile(path, reps) {
let s = fs.readFileSync(path, 'utf8')
const failures = []
for (const [oldStr, newStr] of reps) {
const count = s.split(oldStr).length - 1
if (count === 1) { s = s.replace(oldStr, newStr); continue }
failures.push(`pattern not unique/found (count=${count}): ${oldStr.slice(0, 80)}`)
}
if (failures.length) {
console.error(`PATCH CONTROL FAILED (${path}):`)
failures.forEach((f) => console.error(' -', f))
process.exit(1)
}
fs.writeFileSync(path, s)
console.log('patched ok:', path)
}
patchFile('frontend/app.js', [
/* ---- (1) ref bám vị trí mỗi frame, giữ render-scope consts ---- */
[
` useFrame((_, dt) => { bob.current += dt * (anim || 1) })
const yOff = anim ? Math.sin(bob.current * 1.2) * 0.12 : 0
const gy = floorHeight(Math.hypot(p.pos.x, p.pos.z)) + yOff
const held = p.holding ? 0.15 : 0
return (
<group position={[p.pos.x, gy + held, p.pos.z]} rotation={[0, p.rot, 0]}>`,
` useFrame((_, dt) => {
bob.current += dt * (anim || 1)
// Movement MUTATE me.pos mà KHÔNG gọi set() -> React không re-render.
// Cập nhật imperative qua ref để avatar di chuyển mượt theo đúng phím.
if (rootRef.current) {
rootRef.current.position.set(p.pos.x, floorHeight(Math.hypot(p.pos.x, p.pos.z)) + (p.holding ? 0.15 : 0), p.pos.z)
rootRef.current.rotation.y = p.rot
}
})
const yOff = anim ? Math.sin(bob.current * 1.2) * 0.12 : 0
const gy = floorHeight(Math.hypot(p.pos.x, p.pos.z)) + yOff
const held = p.holding ? 0.15 : 0
return (
<group ref={rootRef} position={[p.pos.x, gy + held, p.pos.z]} rotation={[0, p.rot, 0]}>`,
],
[
` const bob = React.useRef(0)
const anim = p.anim === 'run' ? 14 : (p.anim === 'walk' ? 8 : 0)
useFrame((_, dt) => { bob.current += dt * (anim || 1) })`,
` const bob = React.useRef(0)
const rootRef = React.useRef(null)
const anim = p.anim === 'run' ? 14 : (p.anim === 'walk' ? 8 : 0)`,
],
/* ---- (2) walkR 95 + guard chống vùng trống ---- */
[
` const dist = Math.hypot(nx, nz)
const walkR = 42
if (dist > walkR) { nx *= walkR / dist; nz *= walkR / dist }`,
` const dist = Math.hypot(nx, nz)
const walkR = 95
if (dist > walkR) { nx *= walkR / dist; nz *= walkR / dist }
// chống vùng trống: chưa ra sân ngoài (z<55) thì giữ trong bán kính sàn hang (61)
if (nz < 55) {
const d2 = Math.hypot(nx, nz)
if (d2 > 61) { nx *= 61 / d2; nz *= 61 / d2 }
}`,
],
/* ---- (3) joystick: center reset theo {x,y} ---- */
[
` const center = () => { joy.current = { x: 0, z: 0 }; if (knobRef.current) knobRef.current.style.transform = 'translate(-50%,-50%)' }`,
` const center = () => { joy.current = { x: 0, y: 0 }; if (knobRef.current) knobRef.current.style.transform = 'translate(-50%,-50%)' }`,
],
/* ---- (4) joystick: lưu {x: dx, y: -dy} (dy<0 = kéo lên màn hình) ---- */
[
` joy.current = { x: dx, z: -dy }`,
` joy.current = { x: dx, y: -dy }`,
],
/* ---- (5) joystick: xoay theo hướng camera (kéo lên = đi xa camera) ---- */
[
` if (touch && (joy.current.x || joy.current.z)) { mx = joy.current.x; mz = joy.current.z }`,
` if (touch && (joy.current.x || joy.current.y)) {
const cy = me.rot
const fx = -Math.sin(cy), fz = -Math.cos(cy)
const rx = Math.cos(cy), rz = -Math.sin(cy)
mx = fx * joy.current.y + rx * joy.current.x
mz = fz * joy.current.y + rz * joy.current.x
}`,
],
/* ---- (6) đầu + tóc đen ngắn ---- */
[
` <mesh castShadow position={[0, 2.55 + yOff, 0]}>
<sphereGeometry args={[0.45, 16, 16]} />
<meshStandardMaterial color={0xfdbcb4} roughness={0.9} />
</mesh>`,
` <group position={[0, 2.55 + yOff, 0]}>
<mesh castShadow>
<sphereGeometry args={[0.45, 16, 16]} />
<meshStandardMaterial color={0xfdbcb4} roughness={0.9} />
</mesh>
<mesh castShadow position={[0, 0.13, -0.02]} scale={[1.05, 0.8, 1.05]}>
<sphereGeometry args={[0.45, 16, 12, 0, Math.PI * 2, 0, Math.PI * 0.74]} />
<meshStandardMaterial color={0x1c1610} roughness={0.92} />
</mesh>
</group>`,
],
/* ---- (7) thân: áo đỏ + quần đùi đen + cổ trắng ---- */
[
` <mesh castShadow position={[0, 1.05 + yOff, 0]}>
<capsuleGeometry args={[0.5, 1.5, 4, 8]} />
<meshStandardMaterial color={color} roughness={0.7} metalness={0.1} />
</mesh>`,
` <mesh castShadow position={[0, 1.62 + yOff, 0]}>
<capsuleGeometry args={[0.53, 1.26, 4, 10]} />
<meshStandardMaterial color={color} roughness={0.72} metalness={0.08} />
</mesh>
<mesh castShadow position={[0, 0.72 + yOff, 0]} scale={[1.28, 0.62, 1.05]}>
<sphereGeometry args={[0.42, 10, 8]} />
<meshStandardMaterial color={0x14141f} roughness={0.85} />
</mesh>
<mesh position={[0, 2.2 + yOff, 0]}>
<torusGeometry args={[0.3, 0.06, 6, 18]} />
<meshStandardMaterial color="#e8e8e8" roughness={0.55} />
</mesh>`,
],
/* ---- (8a) tay trái ---- */
[
` <group position={[-0.78, 2.05 + yOff, 0]}>
<mesh castShadow rotation={[Math.sin(bob.current * anim / 4) * 0.9, 0, 0.35]}>
<capsuleGeometry args={[0.16, 0.85, 4, 8]} />
<meshStandardMaterial color={0xfdbcb4} roughness={0.9} />
</mesh>
</group>`,
` <group position={[-0.78, 2.05 + yOff, 0]}>
<mesh castShadow rotation={[Math.sin(bob.current * anim / 4) * 0.9, 0, 0.35]}>
<capsuleGeometry args={[0.19, 0.5, 4, 8]} />
<meshStandardMaterial color={color} roughness={0.72} />
</mesh>
<mesh castShadow position={[0, -0.48, 0]} rotation={[Math.sin(bob.current * anim / 4) * 0.9, 0, 0.35]}>
<capsuleGeometry args={[0.145, 0.46, 4, 8]} />
<meshStandardMaterial color={0xfdbcb4} roughness={0.9} />
</mesh>
</group>`,
],
/* ---- (8b) tay phải ---- */
[
` <group position={[0.78, 2.05 + yOff, 0]}>
<mesh castShadow rotation={[-Math.sin(bob.current * anim / 4) * 0.9, 0, -0.35]}>
<capsuleGeometry args={[0.16, 0.85, 4, 8]} />
<meshStandardMaterial color={0xfdbcb4} roughness={0.9} />
</mesh>
</group>`,
` <group position={[0.78, 2.05 + yOff, 0]}>
<mesh castShadow rotation={[-Math.sin(bob.current * anim / 4) * 0.9, 0, -0.35]}>
<capsuleGeometry args={[0.19, 0.5, 4, 8]} />
<meshStandardMaterial color={color} roughness={0.72} />
</mesh>
<mesh castShadow position={[0, -0.48, 0]} rotation={[-Math.sin(bob.current * anim / 4) * 0.9, 0, -0.35]}>
<capsuleGeometry args={[0.145, 0.46, 4, 8]} />
<meshStandardMaterial color={0xfdbcb4} roughness={0.9} />
</mesh>
</group>`,
],
/* ---- (9a) chân trái: đùi đen + tất đỏ ---- */
[
` <group position={[-0.22, 1.0 + yOff, 0]}>
<mesh castShadow rotation={[Math.sin(bob.current * anim / 4) * 0.9, 0, 0]}>
<capsuleGeometry args={[0.18, 0.95, 4, 8]} />
<meshStandardMaterial color={0x14141f} roughness={0.8} metalness={0.1} />
</mesh>
</group>`,
` <group position={[-0.22, 1.0 + yOff, 0]}>
<mesh castShadow rotation={[Math.sin(bob.current * anim / 4) * 0.9, 0, 0]}>
<capsuleGeometry args={[0.18, 0.55, 4, 8]} />
<meshStandardMaterial color={0x14141f} roughness={0.8} metalness={0.1} />
</mesh>
<mesh castShadow position={[0, -0.42, 0.03]} rotation={[Math.sin(bob.current * anim / 4) * 0.9, 0, 0]}>
<capsuleGeometry args={[0.17, 0.3, 4, 8]} />
<meshStandardMaterial color={0xc8102e} roughness={0.8} />
</mesh>
</group>`,
],
/* ---- (9b) chân phải ---- */
[
` <group position={[0.22, 1.0 + yOff, 0]}>
<mesh castShadow rotation={[-Math.sin(bob.current * anim / 4) * 0.9, 0, 0]}>
<capsuleGeometry args={[0.18, 0.95, 4, 8]} />
<meshStandardMaterial color={0x14141f} roughness={0.8} metalness={0.1} />
</mesh>
</group>`,
` <group position={[0.22, 1.0 + yOff, 0]}>
<mesh castShadow rotation={[-Math.sin(bob.current * anim / 4) * 0.9, 0, 0]}>
<capsuleGeometry args={[0.18, 0.55, 4, 8]} />
<meshStandardMaterial color={0x14141f} roughness={0.8} metalness={0.1} />
</mesh>
<mesh castShadow position={[0, -0.42, 0.03]} rotation={[-Math.sin(bob.current * anim / 4) * 0.9, 0, 0]}>
<capsuleGeometry args={[0.17, 0.3, 4, 8]} />
<meshStandardMaterial color={0xc8102e} roughness={0.8} />
</mesh>
</group>`,
],
/* ---- (10) giày trắng ---- */
[
` <mesh position={[-0.09, 0.22 + yOff, 0.16]} rotation-x={0.25}>
<boxGeometry args={[0.18, 0.09, 0.3]} />
<meshStandardMaterial color={0x1a1a1a} roughness={0.5} metalness={0.15} />
</mesh>
<mesh position={[0.09, 0.22 + yOff, 0.16]} rotation-x={0.25}>
<boxGeometry args={[0.18, 0.09, 0.3]} />
<meshStandardMaterial color={0x1a1a1a} roughness={0.5} metalness={0.15} />
</mesh>`,
` <mesh position={[-0.09, 0.22 + yOff, 0.16]} rotation-x={0.25}>
<boxGeometry args={[0.18, 0.09, 0.3]} />
<meshStandardMaterial color={0xf2f2f2} roughness={0.4} metalness={0.1} />
</mesh>
<mesh position={[0.09, 0.22 + yOff, 0.16]} rotation-x={0.25}>
<boxGeometry args={[0.18, 0.09, 0.3]} />
<meshStandardMaterial color={0xf2f2f2} roughness={0.4} metalness={0.1} />
</mesh>`,
],
])
console.log('patch_control.mjs OK — camera-relative joystick + MU player model applied')