entibo / gats.io 3D

// ==UserScript==
// @name gats.io 3D
// @description 
// @version 0.1
// @author entibo
// @license MIT
// @run-at document-end
// @grant none
// @match https://gats.io/
// ==/UserScript==


window.a16 = ()=>{} // grid background
//window.a55 = ()=>{} // fog
for(let k of ['RA','RB','RC','RD']) {
/* 	window[k].prototype.draw = ()=>{}
	window[k].prototype.drawBody = ()=>{}
	window[k].prototype.drawGun = ()=>{} */
}

function a55(_0xb74969, _0x48631e) {
	if (j31 != j11 && j41 != j12) {
			var _0x2c90da = _0x48631e['getRelPos']({
					'x': 0,
					'y': 0
			});
			var _0x37fb9f = _0x48631e['getRelPos']({
					'x': j11,
					'y': j12
			});
			var _0x2148a2 = (j11 - j31) / 2;
			var _0x589b9a = (j12 - j41) / 2;
			_0xb74969['globalAlpha'] = 0.3;
			_0xb74969['fillStyle'] = '#b5b5b5';
			_0xb74969['fillRect'](_0x2c90da['x'] - 5000, _0x2c90da['y'] - 5000, 5000 + _0x2148a2, j12 + 10000);
			_0xb74969['fillRect'](_0x37fb9f['x'] - _0x2148a2, _0x2c90da['y'] - 5000, _0x37fb9f['x'] + 5000, j12 + 10000);
			_0xb74969['fillRect'](_0x2c90da['x'] + _0x2148a2, _0x2c90da['y'] - 5000, j11 - _0x2148a2 * 2, 5000 + _0x589b9a);
			_0xb74969['fillRect'](_0x2c90da['x'] + _0x2148a2, _0x37fb9f['y'] - _0x589b9a, j11 - _0x2148a2 * 2, 5000 + _0x589b9a);
			_0xb74969['globalAlpha'] = 1;
	}
}
a41=eval(('('+a41+')').replace('a37();',`
a37();
let cvs = document.getElementById("canvas")
let ctx = cvs.getContext("2d")
ctx.save()
ctx.translate(cvs.width/2, cvs.height/2)
ctx.rotate(Math.PI*cameraAngle/180);
ctx.translate(-cvs.width/2, -cvs.height/2);
`))
a41=eval(('('+a41+')').replace('a55(j10,c2);',`
a55(j10,c2);
ctx.restore();
`))


let movement = (() => {
	function mouseAngleToDir(a) {
		return Math.floor(((a+45/2)/45))%8
	}
	let DIR = {
		R: 0,
		RD: 1,
		D: 2,
		LD: 3,
		L: 4,
		LU: 5,
		U: 6,
		RU: 7,
	}
	//let KEYS = ["LEFT","RIGHT","UP","DOWN","RELOAD","SPACE","CLICK"]
	let KEYS = {
		LEFT: 0,
		RIGHT: 1,
		UP: 2,
		DOWN: 3,
	}
	function moveDir(dir) {
		let socket = RF.list[0]
		let dx = ((dir+1)%8<3?1:0) + ((dir+5)%8<3?-1:0)
		let dy = ((dir+7)%8<3?1:0) + ((dir+3)%8<3?-1:0)
		if(dx == 0) {
			socket.send(`k,${KEYS.LEFT},0`)  
			socket.send(`k,${KEYS.RIGHT},0`)  
		}
		else {
			let k1 = KEYS.LEFT, k2 = KEYS.RIGHT
			if(dx < 0) [k1,k2]=[k2,k1]
			socket.send(`k,${k1},0`)
			socket.send(`k,${k2},1`)
		}
		if(dy == 0) {
			socket.send(`k,${KEYS.UP},0`)  
			socket.send(`k,${KEYS.DOWN},0`)  
		}
		else {
			let k1 = KEYS.UP, k2 = KEYS.DOWN
			if(dy < 0) [k1,k2]=[k2,k1]
			socket.send(`k,${k1},0`)
			socket.send(`k,${k2},1`)
		}
	}
	function move(a) {
		let dir = mouseAngleToDir(a)
		moveDir(dir)
	}
	function stop() {
		let socket = RF.list[0]
		socket.send(`k,${KEYS.LEFT},0`)  
		socket.send(`k,${KEYS.RIGHT},0`) 
		socket.send(`k,${KEYS.UP},0`)  
		socket.send(`k,${KEYS.DOWN},0`)  
	}
	let r = {
		move, stop,
		dx: 0, dy: 0,
		keys: [0,0,0,0]
	}
	function computeDelta() {
		r.dx = (r.keys[KEYS.LEFT]?-1:0) + (r.keys[KEYS.RIGHT]?+1:0)
		r.dy = (r.keys[KEYS.UP]?-1:0)   + (r.keys[KEYS.DOWN]?+1:0)
	}
	document.addEventListener("keydown", e => {
		e.stopPropagation()
		let key = e.key.toLowerCase()
		if(key == "q" || key == "a" || key == "arrowleft") r.keys[KEYS.LEFT] = 1
		if(key == "d" || key == "arrowright") r.keys[KEYS.RIGHT] = 1
		if(key == "z" || key == "w" || key == "arrowup") r.keys[KEYS.UP] = 1
		if(key == "s" || key == "arrowdown") r.keys[KEYS.DOWN] = 1
		computeDelta()
	}, true)
	document.addEventListener("keyup", e => {
		e.stopPropagation()
		let key = e.key.toLowerCase()
		if(key == "q" || key == "a" || key == "arrowleft") r.keys[KEYS.LEFT] = 0
		if(key == "d" || key == "arrowright") r.keys[KEYS.RIGHT] = 0
		if(key == "z" || key == "w" || key == "arrowup") r.keys[KEYS.UP] = 0
		if(key == "s" || key == "arrowdown") r.keys[KEYS.DOWN] = 0
		computeDelta()
	}, true)
	return r
})()
window.movement = movement


window.cameraAngle = 0
let start = () => {

  /** @type import("three") */
	const THREE = window.THREE
	
	loadPointerLockThing()
  
	const scene = new THREE.Scene()
	scene.fog = new THREE.FogExp2(0x8DD8F8, 0.0015)

	let renderer = new THREE.WebGLRenderer()
	renderer.setClearColor(scene.fog.color)
	renderer.setPixelRatio(window.devicePixelRatio)
	renderer.setSize(window.innerWidth, window.innerHeight)
	renderer.domElement.style.position = "absolute"
	renderer.domElement.style.zIndex = "0"

	let container = document.body
	container.appendChild(renderer.domElement)

	let camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
	camera.position.y = 20 // Height the camera will be looking from
	camera.position.x = 0
	camera.position.z = 0
	window.camera = camera

	let controls = new THREE.PointerLockControls(camera)
	document.addEventListener("click", () => controls.lock())
	window.controls = controls

	;{ // Lights
	let lightOne = new THREE.DirectionalLight(0xffffff)
	lightOne.position.set(1, 1, 1)
	scene.add(lightOne)
	let lightTwo = new THREE.DirectionalLight(0xffffff, .4)
	lightTwo.position.set(1, -1, -1)
	scene.add(lightTwo)
	scene.add(new THREE.AmbientLight(0xffffff, 0.3))
	};

	let gridHelper
	;{ // Ground
    let groundGeo = new THREE.PlaneGeometry(7000, 7000)
    let groundMat = new THREE.MeshPhongMaterial({ color: 0xf7f7f7, side: THREE.DoubleSide, flatShading: true, })
    let ground = new THREE.Mesh(groundGeo, groundMat)
    ground.position.set(7000/2, 1, 7000/2)
		ground.rotation.x = Math.PI/2
    scene.add(ground)
		gridHelper = new THREE.GridHelper(7000,700)
		window.gridHelper = gridHelper
    gridHelper.position.set(7000/2, 2, 7000/2)
		scene.add(gridHelper)
	};

	;{ // Walls (fog) half are not working
    for(let i=0, sign=1; i < 2; i++) {
				let H = 100
        let perimGeo = new THREE.PlaneGeometry(7000, H);
        // Make the material double sided
        let perimMat = new THREE.MeshPhongMaterial({ color: 0x464646, side: THREE.DoubleSide });
        // Make two walls
        let perimWallLR = new THREE.Mesh(perimGeo, perimMat);
        let perimWallFB = new THREE.Mesh(perimGeo, perimMat);

        // Create left/right walls
        perimWallLR.position.set(7000/2 + sign*j31/2, H / 2, 7000/2);
        perimWallLR.rotation.y = Math.PI;
        scene.add(perimWallLR);
        // Create front/back walls
        perimWallFB.position.set(7000/2, H / 2, 7000/2 + sign*j31/2);
        scene.add(perimWallFB);

        sign = -1; // Swap to negative value
    }
	}

	let makeBlock = (x, z, w, h, color) => {
		let H = 20
		let boxGeo = new THREE.BoxGeometry(w, H, h)
    let boxMat = new THREE.MeshPhongMaterial({
				color,
		})
		let box = new THREE.Mesh(boxGeo, boxMat)
		box.position.x = x
		box.position.z = z
		box.position.y = H/2
		scene.add(box)
		return box
	}

	/** @type Map<number,THREE.Object3D> */
	let boxes = new Map()
	window.boxes = boxes


	let animate = () => {
		requestAnimationFrame(animate)
		renderer.render(scene, camera)

		if(c3 && RD.pool[c3]) {
			camera.position.x = RD.pool[c3].x
			camera.position.z = RD.pool[c3].y

			cameraAngle = (() => {
				let v = camera.getWorldDirection()
				return (360-180*Math.atan2(v.x, v.z)/Math.PI) % 360
			})()
			if(movement.dx != 0 || movement.dy != 0) {
				let a = cameraAngle
				a += 180
				a += 180*Math.atan2(movement.dy, movement.dx)/Math.PI
				a = ((a+720)%360)
				movement.move(a)
			} else {
				movement.stop()
			}
			let socket = RF.list[0]
			RD.pool[c3].playerAngle = (90+cameraAngle)%360
			socket.send(`m,9999,9999,${RD.pool[c3].playerAngle}`)

		}

		Object.values(window.RB.pool).forEach(obj => {
			if(obj.activated) {
				if(!boxes.has(obj.id)) {
					let x = obj.x, y = obj.y
					let w, h, color
					switch(obj.type) {
						case "crate":
							w = 100; h = 100; color = 0xdfbf9f
							break
						case "longCrate":
							w = 100; h = 50; color = 0xbec8dd
							if(obj.angle == 180) {
								[w,h] = [h,w]
							}
							break
						case "userCrate":
							w = 40; h = 40; color = 0x53c68c
						default:
							w = 20; h = 20; color = 0x000000
							break
					}
					let mesh = makeBlock(x, y, w, h, color)
					boxes.set(obj.id, mesh)
				}
			} else {
				let mesh = boxes.get(obj.id)
				if(mesh) scene.remove(mesh)
			}
		})
	}

	animate()

};





function inject(url) {
  let script = document.createElement("script")
  script.src = url
	document.head.appendChild(script)
	return new Promise((resolve, reject) => {
		script.addEventListener("load", resolve)
		script.addEventListener("error", reject)
	})
}





loadPointerLockThing = () => {
	THREE.PointerLockControls = function ( camera, domElement=document.body ) {

		this.domElement = domElement;
		this.isLocked = false;

		// Set to constrain the pitch of the camera
		// Range is 0 to Math.PI radians
		this.minPolarAngle = 0; // radians
		this.maxPolarAngle = Math.PI; // radians

		//
		// internals
		//

		var scope = this;

		var changeEvent = { type: 'change' };
		var lockEvent = { type: 'lock' };
		var unlockEvent = { type: 'unlock' };

		var euler = new THREE.Euler( 0, 0, 0, 'YXZ' );

		var PI_2 = Math.PI / 2;

		var vec = new THREE.Vector3();

		function onMouseMove( event ) {
			event.stopPropagation()
			event.preventDefault()

			if ( scope.isLocked === false ) return;

			var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
			var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

			euler.setFromQuaternion( camera.quaternion );

			euler.y -= movementX * 0.002;
			euler.x -= movementY * 0.002;

			euler.x = Math.max( PI_2 - scope.maxPolarAngle, Math.min( PI_2 - scope.minPolarAngle, euler.x ) );

			camera.quaternion.setFromEuler( euler );

			scope.dispatchEvent( changeEvent );

		}

		function onPointerlockChange() {

			if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {

				scope.dispatchEvent( lockEvent );

				scope.isLocked = true;

			} else {

				scope.dispatchEvent( unlockEvent );

				scope.isLocked = false;

			}

		}

		function onPointerlockError() {

			console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );

		}

		this.connect = function () {

			scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove, true );
			scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange, false );
			scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError, false );

		};

		this.disconnect = function () {

			scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove, true );
			scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange, false );
			scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError, false );

		};

		this.dispose = function () {

			this.disconnect();

		};

		this.getObject = function () { // retaining this method for backward compatibility

			return camera;

		};

		this.getDirection = function () {

			var direction = new THREE.Vector3( 0, 0, - 1 );

			return function ( v ) {

				return v.copy( direction ).applyQuaternion( camera.quaternion );

			};

		}();

		this.moveForward = function ( distance ) {

			// move forward parallel to the xz-plane
			// assumes camera.up is y-up

			vec.setFromMatrixColumn( camera.matrix, 0 );

			vec.crossVectors( camera.up, vec );

			camera.position.addScaledVector( vec, distance );

		};

		this.moveRight = function ( distance ) {

			vec.setFromMatrixColumn( camera.matrix, 0 );

			camera.position.addScaledVector( vec, distance );

		};

		this.lock = function () {

			this.domElement.requestPointerLock();

		};

		this.unlock = function () {

			scope.domElement.ownerDocument.exitPointerLock();

		};

		this.connect();

	};

	THREE.PointerLockControls.prototype = Object.create( THREE.EventDispatcher.prototype );
	THREE.PointerLockControls.prototype.constructor = THREE.PointerLockControls;

}









inject("https://cdnjs.cloudflare.com/ajax/libs/three.js/r120/three.min.js").then(start)